비동기적으로 작업을 처리하는 코루틴에서 Suspend함수를 사용하면 작업이 모두 완료된 후에 단일 값만 반환 받을수 있음. 하지만 중간중간 진행되는 코루틴 작업 중에 갱신되는 값까지 반환 받을 수 있다면 로직 업데이트를 더 촘촘히 진행할 수 있습니다.
코루틴에서 Flow은 단일 값만 반환하는 정지 함수와 달리 여러 값을 순차적으로 내보낼 수 있는 유형입니다.
Flow는 코루틴을 기반으로 빌드되며 여러 값을 제공할 수 있습니다. Flow는 비동기식으로 계산할 수 있는 데이터 스트림의 개념입니다. 내보낸 값은 동일한 유형이어야 합니다. 예를 들어 Flow<Int>
는 정수 값을 내보내는 Flow입니다. suspend가능한 iterator개념
위 그림에서 Producer는 스트림에 추가되는 데이터를 생산합니다. 코루틴 덕분에 흐름에서 비동기적으로 데이터가 생산될 수도 있습니다.
(선택사항) Intermediary는 스트림에 내보내는 각각의 값이나 스트림 자체를 수정할 수 있습니다.
Consumer는 스트림의 값을 사용합니다.
flow는 값을 방출할 때는 emit을 사용하고 회수 할 때는 collect를 사용합니다.
fun simple(): Flow<Int> = flow { // flow builder
for (i in 1..3) {
delay(100) // pretend we are doing something useful here
emit(i) // emit next value
}
}
fun main() = runBlocking<Unit> {
// Launch a concurrent coroutine to check if the main thread is blocked
launch {
for (k in 1..3) {
println("I'm not blocked $k")
delay(100)
}
}
// Collect the flow
simple().collect { value -> println(value) }
}
출력값
I'm not blocked 1
1
I'm not blocked 2
2
I'm not blocked 3
3
Flow는 collect로 값을 요청하지 않는 한 값을 방출하지 않습니다. 이러한 것을 Cold Stream이라고 하고 Hot Stream은 요청이 있건 없건 계속 값을 방출하는 것을 말합니다.
Cold Stream은 원하는 정해진 값을 얻을 수 있고 Hot Stream은 가변적으로 변하는 값을 얻을 수 있습니다.
fun simple(): Flow<Int> = flow {
println("Flow started")
for (i in 1..3) {
delay(100)
emit(i)
}
}
fun main() = runBlocking<Unit> {
println("Calling simple function...")
val flow = simple()
println("Calling collect...")
flow.collect { value -> println(value) }
println("Calling collect again...")
flow.collect { value -> println(value) }
}
출력값
Calling simple function...
Calling collect...
Flow started
1
2
3
Calling collect again...
Flow started
1
2
3
위 코드에서 보면 collect를 하기 전까지는 flow가 실행되지 않습니다.
LiveData
Flow
Coroutine Scope
Flow는 Coroutine Scope안에서 동작하기 때문에 viewModelScope나 lifeCycleScope와 함께 사용하면 LiveData처럼 ViewModel 또는 Activity나 Fragment 생명주기를 감지하고 맞춰서 동작을 실행하거나 정제할 수 있습니다.
Operator
LiveData에는 연산자들이 많이 없지만 Flow에는 풍부한 연산자가 있어 데이터를 필요에 따라 유연하게 변환할 수 있습니다.
Android Dependency
Flow는 코틀린 언어에 포함된 기능이기 때문에 LiveData와 달리 안드로이드 의존성으로부터 자유롭습니다.
Cold Stream
Collect 가 되어야만 값을 방출합니다.
상태가 없음
상태가 없으므로 value를 통해 값을 가져오지 못합니다.
fun <T> Flow<T>.stateIn(scope : CoroutineScope, started : SharingStarted, initialValue : T) : StateFlow<T>
여기서 SharingStarted에는 3가지 선택지가 있습니다.
1. SharingStarted.Eagerly : collector가 존재하지 않더라도 바로 sharing이 시작되며 중간에 중지되지 않습니다.
2. SharingStarted.Lazily : collector가 등록된 이후부터 sharing이 시작되며 중간에 중지되지 않습니다.
3. SharingStarted.WhileSubsribed : collector가 등록되면 바로 sharing을 시작하며 collector가 전부 없어지면 바로 중지됩니다.
구글에서는 WhileSubsribed (5000)을 권장하고 있는데 flow의 중단 타이밍과 관련이 있습니다.
액티비티가 화면 회전 등 파괴되고 다시 생성되는 경우와 홈 버튼을 눌러서 백그라운드 상태로 되는 경우를 구분하기 위해 딜레이를 5초로 주면 어떤 상황인지 구분이 가능해 flow를 중지할지 결정할 수 있어 WhileSubsribed (5000)을 권장하고 있습니다.
레퍼런스
https://kotlinlang.org/docs/flow.html#flows
냉동코더의 알기 쉬운 Modern Android Developer 입문