Kotlin Coroutine

Kotlin Coroutine - LiveData, Flow, StateFlow, SharedFlow, stateIn, sharedIn

olivia 2022. 1. 27. 18:00

목표

  • LiveData와 stateFlow & sharedFlow의 차이점 이해
  • StateFlow와 SharedFlow의 차이점과 사용방법 이해
  • stateIn과 sharedIn 이해 

 

Coroutine Flow

Flow란  

  • Flow는 단일 값만 반환하는 suspend 함수와 달리 여러 값을 순차적으로 내보낼 수 있는 유형
  • 비동기식으로 계산할 수 있는 데이터 스트림의 개념
  • Flow는 cold stream 이다

stream?

  • Cold Stream
    • 구독자가 하나 존재하며 해당 구독자에게만 값을 방출 또한 해당 구독자는 스트림을 초기화하는 구독자 
      • 즉, 다른 구독자가 또 생겨날 경우 구독 시점은 다르나 데이터는 동일하다
    • Lazy Stream으로 누군가가 구독할 때만 값의 방출이 시작
  • Hot Stream
    • Consumer 가 여러 개 존재하며 동시에 모든 구독자에게 값을 방출
    • Eager Stream으로 구독자가 없는 경우에도 상관없이 데이터 방출

 

LiveData 한계 

  1. LiveData는 UI에 밀접하여 클린 아키텍처 기반으로 보았을 때 presentation layer 이외의 영역에서는 자연스럽게 동작할 방법이 없다
  2. Android 플랫폼에 밀접하게 붙어있다 
  3. thread 변경이 불가능 하다
  4. 중간 연산자 사용이 불가능 하다 map

Flow 자체로 LiveData를 대체하지 못하는 이유

  1. Flow는 상태가 없으므로 .value 값을 얻을 수 없음
  2. Flow 는 cold stream으로 각각 다른 곳에서 collect 되었을 경우 구독 시점에 따라 다른 값이 나올 수 있음 또한 새로운 Flow 가 실행될 경우 collect마다 여러 번 호출될 수 있음.
  3. Flow Android lifecycle 을 알지 못함 (저절로 중지 재실행되지 못함) 
Flow는 선언적이고, Collect 될 때만 실행됩니다. 만약 우리가 여러 개의 colletor를 가지고 있다면, 새로운 Flow 하나가 실행될 때마다 다른 모든 Collector가 동작할 것입니다. 그것도 완벽히 독립적으로, 각각 실행될 것입니다

 

즉, Flow 자체로는 대체가 불가능하기 때문에 StateFlow, SharedFlow로 위와 같은 한계점을 보완하여 사용한다. 

 

StateFlow와 SharedFlow

SharedFlow

  • SharedFlow는 Flow 종류 중 하나로 하나의 자신을 여러 개의 Collector에 공유하기 때문에 오진 하나의 Flow만을 실행함
  • SharedFlow 는 cold Flow를 hot Flow로 변환해줌
  • SateFlow에서 추가 버퍼가 필요하거나 여러 개의 최신 값을 반환해야 하는 곳, 초기 값을 무시해야 하는 곳에 사용 
  • . value 현재 값 반환 불가능
  • 구독자에게 최근 n 개까지의 값 전달 가능 (replay 기능) 

StateFlow

  • SharedFlow와 같은 동작으로 사용 가능하나 반복적인 값에 대해서는 발행하지 않음.
  • . value 현재 값 반환 가능
  • 초기값 설정 필수 

 

StateIn과 SharedIn

SharedIn

  • flow를 sharedFlow 로 변환하는 오퍼레이터
fun <T> Flow<T>.shareIn(scope: CoroutineScope, started: SharingStarted, replay: Int = 0): SharedFlow<T>

StateIn

  • flow 를 stateFlow로 변환하는 오퍼레이터
  • replay 값 설정 불가능(sharedFlow 가 replay 1로 고정된 것) -> 새로운 구독자는 즉시 현재 상태 값을 받게 됨 
  • 초기 값 필요 (null 설정이 필요할 경우 들어가는 값을 nullable로 하거나 seald class의 Null 초기값 정의) 
fun <T> Flow<T>.stateIn(scope: CoroutineScope, started: SharingStarted, initialValue: T): StateFlow<T>