`stateIn`으로 `Flow`를 `StateFlow`로 변환하는 것에 대한 간단한 이해를 정리한 글
Flow
`Flow`는 cold 스트림
- 해당 플로우에 대해서 `collect`가 호출되기 전까지는 아무런 동작 하지 않음
- `collect`가 호출되면 처음부터 발행
- 값 발행이 완료되면 스트림이 종료됨
val myFlow = flow {
// collect가 호출되기 전까지
// 해당 코드는 실행되지 않음
// 0부터 100까지 1초 간격으로 값을 발행(emit)
repeat(100) {
Log.d(TAG, ">> emit -> $it")
emit(it)
delay(1000)
}
}
CoroutineScope(Dispatchers.Main).launch {
launch {
// 처음부터 collect 시작
myFlow.collect { Log.d(TAG, "#1st -> $it") }
}
delay(2000) // 다음 collect 호출까지 2초 대기
launch {
// 처음부터 collect 시작
myFlow.collect { Log.d(TAG, "#2nd -> $it") }
}
}
첫번째 `collect`가 호출되면 0부터 값이 발행(emit)되기 시작한다.
2초 후 두번째 `collect`가 호출되면 역시 0부터 값이 방출되기 시작한다.
StateFlow
`StateFlow`는 hot 스트림
- collect하는 곳이 없더라도 값을 계속 발행함
- collect를 호출하면 처음부터가 아닌 가장 최근에 발행된 최신 값을 받게 됨
val myFlow = flow {
// collect가 호출되기 전까지
// 해당 코드는 실행되지 않음
// 0부터 100까지 1초 간격으로 값을 방출(emit)
repeat(100) {
Log.d(TAG, ">> emit -> $it")
emit(it)
delay(1000)
}
}
// Flow를 StateFlow로 변환
val myStateFlow = myFlow.stateIn(
scope = lifecycleScope,
started = SharingStarted.Lazily,
initialValue = -1
)
CoroutineScope(Dispatchers.Main).launch {
delay(1000) // 1초 대기
launch {
// 가장 최근에 방출된 값부터 collect된다. (= 1)
myStateFlow.collect { Log.d(TAG, "#1st -> $it") }
}
delay(2500) // 2.5초 대기
launch {
// 가장 최근에 방출된 값부터 collect된다. (= 3)
myStateFlow.collect { Log.d(TAG, "#2nd -> $it") }
}
}
`myFlow`를 `stateIn`을 통해 `StateFlow`로 변환시킴으로서 `myStateFlow`가 `myFlow`를 collect하고 있는 것이라고 이해했다.
collect하는 곳이 있으니 `myFlow`는 계속해서 값을 발행하게 되고, `myStateFlow`는 그 값을 받아 가장 최신 값을 hold하고 있는다.
`myStateFlow`에 대해 `collect`가 호출되면 들고있던 최신 값을 발행한다.
로그는 다음과 같다.
>> emit -> 0
>> emit -> 1
#1st -> 1 // collect를 시작한 시점에서의 가장 최신 값
>> emit -> 2
#1st -> 2
>> emit -> 3
#1st -> 3
#2nd -> 3 // collect를 시작한 시점에서의 가장 최신 값
>> emit -> 4
#1st -> 4
#2nd -> 4
.
.
.
끝
Flow에 stateIn을 사용해서 StateFlow로 만든다는게 어떤 의미인건지 잘 감이 안잡혔는데 계속 들여다보니 나름대로 정리할 수 있었다.