Image
Coroutines/Flow

[Coroutine Flow] 3. stateIn 사용하여 Flow를 StateFlow로 변환하기

Flow와 StateFlow

우리는 리액티브 프로그래밍을 할 때 여러 데이터 흐름(flow)를 하나로 합쳐 하나의 데이터 흐름(Flow)으로 만들어낸다. 예를 들어 아래의 그림3에서는 Flow가 3개 있고 이것이 합쳐져 하나의 Flow를 만들어낸다.

이러한 상황은 흔하다. 예를 들어 우리가 영화 평점 앱을 만든다고 할 경우 영화에 대한 정보, 사용자에 대한 정보, 사용자의 영화의 평점에 대한 정보를 각 테이블에서 가져와 하나의 객체로 만들어야 한다.

그림1. Flow와 StateFlow

하나로 만들어진 Flow는 UI에서 사용되기 위해 StateFlow로 변환되어야 한다. 이 UI에서는 이 StateFlow를 구독하여 항상 최신 데이터를 발행받는다. 

 

이것이 가능하기 위해서는 Flow를 StateFlow로 변환하는 로직이 필요하다. 또한 StateFlow가 항상 Flow를 구독하고 있으면 메모리 누수가 생기므로 이 StateFlow가 살아있어야 하는 CoroutineScope을 명시할 수 있어야 한다. 우리는 이를 stateIn 함수를 통해 할 수 있다.

 

*StateFlow는 Cold Stream 이 아니라 Hot Stream이다. 마지막 홀딩하고 있는 데이터를 구독하는 구독자에게 전달할 뿐, 구독자가 구독할 때 발행을 위한 로직을 Trigger 하지는 않는다.

 

 

stateIn 사용하여 Flow를 StateFlow로 변환하기

stateIn 함수를 사용하면 Flow를 StateFlow로 변환할 수 있다. stateIn내부는 다음과 같다.

public fun <T> Flow<T>.stateIn(
    scope: CoroutineScope,
    started: SharingStarted,
    initialValue: T
): StateFlow<T> {
    val config = configureSharing(1)
    val state = MutableStateFlow(initialValue)
    val job = scope.launchSharing(config.context, config.upstream, state, started, initialValue)
    return ReadonlyStateFlow(state, job)
}

stateIn은 세가지 변수를 받는다.

  • scope : StateFlow가 Flow로부터 데이터를 구독받을 CoroutineScope을 명시한다.
  • started : Flow로부터 언제부터 구독을 할지 명시할 수 있다.
  • initialValue : StateFlow에 저장될 초기값을 설정한다.

 

 

이 3가지 변수를 이용한 예제를 하나를 만들어보자. 1초마다 String 값을 발행하는 간단한 Flow를 다음과 같이 선언한다.

val stringFlow: Flow<String> = flow {
    for (i in 0..1000) {
        emit("integer: $i")
        delay(1000)
    }
}

 

이 flow를 stateIn함수를 이용하여 StateFlow로 변환한다.

  • 변수의 시작 시 저장 값은 "integer 0"이여야 한다. 따라서 initialValue를 0으로 한다.
  • started의 WhileSubscribed는 collector 가 없어졌을 때 지정된 시간 이후 StateFlow 발행을 멈추도록 만드는 값이다. 따라서 collector가 없어진 후 5초 후에 동작을 멈추도록 만들기 위해 SharingStarted.WhileSubscribed(5000)을 설정한다.
  • 이 StateFlow가 Flow로부터 구독을 하는 범위는 ViewModel이 살아있을 때까지만이다. 따라서 viewModelScope을 scope(구독 범위)로 넘긴다.
val stateFlow = stringFlow.stateIn(
    initialValue = "integer 0",
    started = SharingStarted.WhileSubscribed(5000),
    scope = viewModelScope
)

이렇게 하면 초기 저장값은 "integer 0"이고, 수집이 중단되고 5초간만 추가로 발행되고, ViewModel의 생명주기만큼만 구독받는 행동을 하는 StateFlow가 만들어진다.

 

 

 

Kotlin Coroutines 공식 기술 문서 번역이 GitHub 오픈소스로 배포되었습니다. Starganizer가 되어 오픈소스를 지지해주세요.

 

Kotlin Coroutines 공식 문서 번역을 시작하며 · GitBook

Kotlin Coroutines 공식 문서 번역을 시작하며 Kotlin Coroutines는 Kotlin을 위한 강력한 비동기 솔루션이다. 안드로이드 실무에서는 한동안 높은 점유율을 자랑한 RxJava를 Coroutines가 대체하고 있으며, 새로

seyoungcho2.github.io

반응형

 

이 글의 저작권은 '조세영의 Kotlin World' 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.

 

 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com