TIL240513 StateFlow

jericho·2024년 5월 13일

TIL

목록 보기
60/62

Flow

https://developer.android.com/kotlin/flow?hl=ko

StateFlow

https://developer.android.com/kotlin/flow/stateflow-and-sharedflow?hl=ko

StateFlow와 SharedFlow는 flow가 여러 소비자에게 최적으로 상태 업데이트와 값을 내보내도록 하는 Flow API 이다.

StateFlow는 현재 상태와 새로운 상태 업데이트를 collector들에게 내보내는 "state-holder observable flow" 이다. Flow와 달리 value 속성을 통해서도 현재 상태 값을 읽을 수 있다. 상태를 업데이트하고 흐름에 전송하려면 MutableStateFlow 클래스의 value 속성에 새 값을 할당한다.

Android에서 StateFlow는 관찰 가능한 변경 가능 상태(observable mutable state)를 유지해야 하는 클래스에 아주 적합하다.

MutableStateFlow 업데이트를 담당하는 클래스가 생산자(producer)이고, StateFlow에서 수집(collect)하는 모든 클래스가 소비자(consumer)이다. flow 빌더를 사용하여 빌드된 cold flow와 달리 StateFlow는 hot flow이다. flow에서 collect 하더라도 생산자 코드가 트리거되지 않는다. StateFlow는 항상 활성 상태이고 메모리 내에 있으며, 가비지 컬렉션 루트에서 달리 참조가 없는 경우에만 가비지 컬렉션에 사용할 수 있다.

새로운 소비자가 흐름에서 수집을 시작하면 스트림의 마지막 상태와 후속 상태가 수신된다.
(LiveData와 같은 관찰 가능한 다른 클래스들에서 이러한 동작을 찾을 수 있다.)

View는 다른 flow들과 마찬가지로 StateFlow를 '듣는'다. (listen)

class LatestNewsActivity : AppCompatActivity() {
    private val latestNewsViewModel = // getViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Start a coroutine in the lifecycle scope
        lifecycleScope.launch {
            // repeatOnLifecycle launches the block in a new coroutine every time the
            // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Trigger the flow and start listening for values.
                // Note that this happens when lifecycle is STARTED and stops
                // collecting when the lifecycle is STOPPED
                latestNewsViewModel.uiState.collect { uiState ->
                    // New value received
                    when (uiState) {
                        is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
                        is LatestNewsUiState.Error -> showError(uiState.exception)
                    }
                }
            }
        }
    }
}

경고: UI를 업데이트해야 하는 경우 launch 또는 launchIn 확장 함수로 UI에서 직접 흐름을 수집하면 안 된다. 이러한 함수는 뷰가 표시되지 않는 경우에도 이벤트를 처리하는데, 이로 인해 앱이 터질 수 있다. 이를 방지하려면 위와 같이 repeatOnLifecycle API를 사용한다.
(참고: repeatOnLifecycle API는 androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 라이브러리 이상 버전에서만 사용 가능)

Flow를 StateFlow로 변환하려면 stateIn 중간 연산자를 사용한다.
(참고: StateFlow를 테스트하려면 (특히 stateIn으로 생성됐다면) 주의가 필요하다. 자세한 내용은 링크 참고: https://developer.android.com/kotlin/flow/test?hl=ko#stateflows)

StateFlow와 LiveData의

  • 공통점
    • 관찰 가능한 데이터 홀더 클래스
    • 앱 아키텍처에서 비슷한 패턴을 따름
  • 차이점
    • StateFlow는 초기 상태를 생성자에 전달해야만 함
    • 뷰가 STOPPED 상태가 되면 LiveData.observe()는 소비자를 자동으로 등록 취소하는 반면, StateFlow 혹은 Flow에서 수집하는 경우 자동으로 수집을 중지하지 않음. (동일한 동작을 실행하려면 Lifecycle.repeatOnLifecycle 블록에서 흐름을 수집해야 함)

추후에 참고할 것들

shareIn을 사용하여 clod flow를 hot flow로 만들기
https://developer.android.com/kotlin/flow/stateflow-and-sharedflow?hl=ko#sharein

shareIn 함수는 수집하는 모든 소비자에게 값을 내보내는 hot flow인 SharedFlow를 반환한다. (SharedFlow는 StateFlow의 유연한 구성 일반화(highly-configurable generalization)이다)
https://developer.android.com/kotlin/flow/stateflow-and-sharedflow?hl=ko#sharedflow


사용법은 LiveData와 비슷한데, 코루틴 기반?인 것 같다. 그리고 setValue, postValue로 동기/비동기 값 갱신을 하는 LiveData와 달리, setValue (.value = value)만 있고, 동시성 코루틴에서 외부 동기화 없이 안전하게 값을 업데이트 할 수 있다고 한다?..는데 뭔 말인지 잘 모르겠다.

또한 LiveData는 안드로이드 종속성이고 Flow는 순수 코틀린?이라는 것 같다. 그래서 도메인 레이어에서 안드로이드 종속성을 가지면 안되기 때문?에 LiveData는 사용할 수 없고 Flow는 괜찮다는 것 같다.

아직 감이 잘 오지 않는데, 프로젝트에서 직접 변환해보면서 익혀봐야겠다.

0개의 댓글