[Android] stateIn이란?

이승우·2023년 6월 3일
1
post-custom-banner

Flow와 StateFlow

우리는 여러 데이터 흐름(flow)를 하나로 합쳐 하나의 데이터 흐름(flow)를 만들어낸다. 아래에서는 3개의 Flow가 있고 이걸 합쳐서 하나의 Flow를 만든다.

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

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

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

stateIn을 사용하여 Flow -> StateFlow 변환

stateIn 함수를 사용하면 Flow를 StateFlow로 변환할 수 있다.

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)
}
  • scope : StateFlow가 Flow로부터 데이터를 구독받을 CoroutineScope을 명시한다.
  • started : Flow로부터 언제부터 구독을 할지 명시할 수 있다.
  • initialValue : StateFlow에 저장될 초기값을 설정한다.

아래는 1초마다 String 값을 발행하는 Flow 예제이다.

val stringFlow = 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"이고, collect가 중단되고 5초간만 추가로 발행되고 그 이후에는 발행을 멈추며, ViewModel의 생명주기만큼만 구독받는 행동을 하는 StateFlow가 만들어진다.

Ref

이미지는 kotlinworld님의 이미지를 캡쳐하여 사용했습니다.

profile
Android Developer
post-custom-banner

0개의 댓글