[안드로이드] Flow

이상욱·2022년 12월 23일
0

안드로이드

목록 보기
11/17
post-thumbnail

✅ Kotlin Flow

비동기적으로 작업을 처리하는 코루틴에서 Suspend함수를 사용하면 작업이 모두 완료된 후에 단일 값만 반환 받을수 있음. 하지만 중간중간 진행되는 코루틴 작업 중에 갱신되는 값까지 반환 받을 수 있다면 로직 업데이트를 더 촘촘히 진행할 수 있습니다.
코루틴에서 Flow은 단일 값만 반환하는 정지 함수와 달리 여러 값을 순차적으로 내보낼 수 있는 유형입니다.
Flow는 코루틴을 기반으로 빌드되며 여러 값을 제공할 수 있습니다. Flow는 비동기식으로 계산할 수 있는 데이터 스트림의 개념입니다. 내보낸 값은 동일한 유형이어야 합니다. 예를 들어 Flow<Int>는 정수 값을 내보내는 Flow입니다. suspend가능한 iterator개념

위 그림에서 Producer는 스트림에 추가되는 데이터를 생산합니다. 코루틴 덕분에 흐름에서 비동기적으로 데이터가 생산될 수도 있습니다.
(선택사항) Intermediary는 스트림에 내보내는 각각의 값이나 스트림 자체를 수정할 수 있습니다.
Consumer는 스트림의 값을 사용합니다.

Flow의 동작

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는 Cold Stream입니다.

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가 실행되지 않습니다.

Flow vs LiveData

LiveData

  • 생명주기를 가진 데이터 홀더
  • 메인스레드에서 동작하기 때문에 워커 쓰레드에서 작업을 처리해야 되는 Data Layer에서는 사용하기 적절하지 않습니다.
  • 안드로이드와 밀접하게 결합되어 있어 테스트가 까다로워 집니다.

Flow

  • Coroutine Scope
    Flow는 Coroutine Scope안에서 동작하기 때문에 viewModelScope나 lifeCycleScope와 함께 사용하면 LiveData처럼 ViewModel 또는 Activity나 Fragment 생명주기를 감지하고 맞춰서 동작을 실행하거나 정제할 수 있습니다.

  • Operator
    LiveData에는 연산자들이 많이 없지만 Flow에는 풍부한 연산자가 있어 데이터를 필요에 따라 유연하게 변환할 수 있습니다.

  • Android Dependency
    Flow는 코틀린 언어에 포함된 기능이기 때문에 LiveData와 달리 안드로이드 의존성으로부터 자유롭습니다.

  • Cold Stream
    Collect 가 되어야만 값을 방출합니다.

  • 상태가 없음
    상태가 없으므로 value를 통해 값을 가져오지 못합니다.

SharedFlow로 LiveData 대체하기

  • SharedFlow
    Hot Stream으로 동작하게 하는 Flow의 종류입니다.
  • StateFlow
    SharedFlow에 상태를 부여해서 현재 값을 얻을 수 있게 제한을 가한 것입니다.
    뷰가 STOPPED 상태가 되면 LiveData.observe()는 소비자를 자동으로 등록 취소하는 반면, StateFlow 또는 다른 flow에서 수집하는 경우 자동으로 수집을 중지하지 않습니다. 동일한 동작을 실행하려면 Lifecycle.repeatOnLifecycle 블록에서 흐름을 수집해야 합니다.
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 입문

profile
항상 배우고 성장하는 안드로이드 개발자

0개의 댓글