깨알 개념/Kotlin

[Kotlin] Coroutines Flow - StateFlow

interfacer_han 2024. 8. 17. 04:42

#1 개요

 

StateFlow

A SharedFlow that represents a read-only state with a single updatable data value that emits updates to the value to its collectors. A state flow is a hot flow because its active instance exists independently of the presence of collectors. Its current valu

kotlinlang.org

SharedFlow의 일종인 StateFlow에 대해 살펴본다.

 

#2 SharedFlow와의 비교

이름에서 알 수 있듯, StateFlow는 기존 SharedFlowState의 특성을 결합한 Flow다. 따라서 State.value로 State의 값을 참조했던 것과 같이, StateFlow.value로 StateFlow의 최신 emit값을 얻을 수 있다. 또 State를 선언할 때 초깃값을 할당해야하는 것처럼, StateFlow를 선언할 때도 그래야한다. StateFlow를 emit하면 해당 값이 자동으로 StateFlow.value에 들어가지만, 반대로 StateFlow.value에 명시적으로 값을 할당한다고 emit()이 수행되지는 않는다. 결과적으로, StateFlow.value는 초깃값 혹은 마지막으로 emit()된 값이라는 의미에 충실한다.

 

#3 예시 코드

#3-1 SharedFlow

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow

fun main() {
    val sharedFlow = MutableSharedFlow<Int>()

    // 리스너 1
    CoroutineScope(Dispatchers.Default).launch {
        sharedFlow.collect { value ->
            println("(리스너 1) Collected value: $value")
        }
    }

    runBlocking {
        delay(1000)
        sharedFlow.emit(1)
        delay(1000)
        sharedFlow.emit(2)
        delay(1000)
        sharedFlow.emit(3)
        delay(1000)
    }

    // 리스너 2
    CoroutineScope(Dispatchers.Default).launch {
        sharedFlow.collect { value ->
            println("(리스너 2) Collected value: $value")
        }
    }

    runBlocking {
        delay(1000)
        sharedFlow.emit(4)
        delay(1000)
        sharedFlow.emit(5)
        delay(1000)
    }
}

/* ↑ ↑ ↑ 출력 결과
(리스너 1) Collected value: 1
(리스너 1) Collected value: 2
(리스너 1) Collected value: 3
(리스너 1) Collected value: 4
(리스너 2) Collected value: 4
(리스너 1) Collected value: 5
(리스너 2) Collected value: 5
*/

간단한 SharedFlow 코드다.

 

#3-2 StateFlow

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow

fun main() {
    val stateFlow = MutableStateFlow(0)

    // 리스너 1
    CoroutineScope(Dispatchers.Default).launch {
        stateFlow.collect { value ->
            println("(리스너 1) Collected value: $value")
        }
    }

    runBlocking {
        delay(1000)
        stateFlow.emit(1)
        delay(1000)
        stateFlow.emit(2)
        delay(1000)
        stateFlow.emit(3)
        delay(1000)
    }

    // 리스너 2
    CoroutineScope(Dispatchers.Default).launch {
        stateFlow.collect { value ->
            println("(리스너 2) Collected value: $value")
        }
    }

    runBlocking {
        delay(1000)
        stateFlow.emit(4)
        delay(1000)
        stateFlow.emit(5)
        delay(1000)
    }
}

/* ↑ ↑ ↑ 출력 결과
(리스너 1) Collected value: 0
(리스너 1) Collected value: 1
(리스너 1) Collected value: 2
(리스너 1) Collected value: 3
(리스너 2) Collected value: 3
(리스너 1) Collected value: 4
(리스너 2) Collected value: 4
(리스너 1) Collected value: 5
(리스너 2) Collected value: 5
*/

#3-1의 출력 결과와 비교하면, "(리스너 1) Collected value: 0"와 "(리스너 2) Collected value: 3"이 추가로 존재한다. 이는 신규 리스너 등장 시의 동작 차이 때문이다 (#2 참조).

 

#4 존재 의의 (State가 가지지 못하는 StateFlow의 장점)

 

[Android] Jetpack Compose - State 기초

#1 개요 상태 및 Jetpack Compose  |  Android Developers이 페이지는 Cloud Translation API를 통해 번역되었습니다. 상태 및 Jetpack Compose 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고

kenel.tistory.com

위 게시글의 #5에서 State가 LiveData보다 비교 우위에 있음에 대해 서술했다. 즉, 이미 State 만으로도 LiveData를 대체할만 하다. 하지만 구글 개발자가 작성한 이 게시글은 LiveData를 StateFlow로 마이그레이션하라고 권장하고 있다. 왜 StateFlow까지 사용하는가? 그 이유는 아래와 같다.

 

1. LiveData와 State 모두 비동기 데이터 스트림을 처리할 수 있게 설계되지 않았다 (따라서, 외부 서버 등에서 받아오는 데이터 스트림을 View까지 매끄럽게 전달하지 못함).

2. StateFlow는 동기 코드인 State와 달리 비동기적으로 작동한다. 따라서, 병렬 처리에 더 유연하다.

3. State는 사용할 수 없는, Flow의 중간 연산자(Intermediate operator)를 쓸 수 있다.

4. 백 프레셔 처리가 용이하다.

 

이어지는 글에서, LiveData가 쓰인 안드로이드 프로젝트를 StateFlow로 마이그레이션해본다 (본 게시글의 #6-2 참조).

 

#5 요약

StateFlow는 State + Flow로, 각각의 장점을 전부 활용한다.

 

#6 이어지는 글

#6-1 Jetpack Compose에서 StateFlow 사용하기

 

[Android] Jetpack Compose - StateFlow와 SharedFlow

#1 이전 글 [Kotlin] Coroutines Flow - StateFlow#1 개요 StateFlowA SharedFlow that represents a read-only state with a single updatable data value that emits updates to the value to its collectors. A state flow is a hot flow because its active instan

kenel.tistory.com

 

#6-2 기존 XML 구조에서 StateFlow 사용하기

 

[Android] LiveData - Flow로 마이그레이션

#1 이전 글 [Kotlin] Coroutines Flow - StateFlow#1 개요 StateFlowA SharedFlow that represents a read-only state with a single updatable data value that emits updates to the value to its collectors. A state flow is a hot flow because its active instan

kenel.tistory.com

LiveData가 사용된 전통적인 XML 구조의 프로젝트를 StateFlow로 마이그레이션해본다.