StateFlow

TRASALBY·2024년 3월 19일
0

학습내용 옮기기

목록 보기
8/8

Flow의 한계

Flow는 데이터의 흐름이다. Flow는 흐름만 발생시킬 뿐 데이터가 저장되지 않는다.

따라서 Flow만을 사용하여 안드로이드의 UIState를 업데이트 하는 것을 불가능 하다.

이를 해결하기 위한 방법이 2가지가 있다.

  1. 화면이 재구성 될 때마다 새로 데이터 가져오기
  2. Flow로 부터 collect된 데이터를 ViewModel에서 저장해 두고 사용하기

1번 방법은 비효율적이다.

  • 화면 재구성이 일어날 때 마다 (예를 들어 화면 회전만 일어나더라도) 매번 새로운 데이터를 가져와야 하기 때문

2번 방법은 효율적이다.

  • 뷰모델은 화면보다 더 긴 생명주기를 가지고 있기 때문에 값을 유지할 수 있기 때문

별도의 데이터 홀더 변수의 필요

  • ViewModel에서 사용할 데이터 홀더 변수를 생성한다.
    • Reactive하지 않은 데이터 홀더 변수를 구독하기 위한 별도의 fetching 로직이 필요
  • ViewModel에서 데이터 홀더변수와 Flow를 함께 사용하기.
    • Flow를 구독하고 데이터 홀더 변수는 Flow에서 마지막으로 발행한 데이터를 저장한다.
    • UI에서는 Flow가 값을 발행하기 전에는 데이터 홀더 변수의 데이터를 사용한다.

보일러 플레이트 코드 발생

앞선 방법 들은 모두 모일러 플레이트 코드를 발생한다.

안드로이드에서 수집하는 UIState가 한 두가지가 아닐 텐데 모두 구독하기 위해 매번 비슷한 코드의 작성이 강제되는 것은 가독성을 떨어트리게 된다

StateFlow

StateFlow는 데이터 홀더(저장소) 역할과 동시에 Flow의 데이터 스트림 역할까지 맡는다.

  • UI단에서 StateFlow를 구독하여 UIState를 업데이트 하면 화면이 재구성될 때 마다 다시 서버로 데이터를 요청할 필요가 없어진다.
  • UI는 단순히 StateFlow만을 구독하고 있는 것으로 UI상태를 알 수있다.

StateIn을 사용해 Flow를 StateFlow로 변환

리액티브 프로그래밍을 할 때 여러 데이터 흐름을 하나로 합쳐 하나의 데이터 흐름으로 만들어 낸다.

영화 평점앱을 만든다고 할 경우

  • 영화에 대한 정보
  • 사용자에 대한 정보
  • 사용자의 영화의 평점에 대한 정보

등을 하나의 StateFlow로 변환하고 UI는 이 StateFlow를 구독하여 항상 최신 데이터를 발행받는다.

이를 위해 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은 3가지 변수를 받는다.

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

이제 Flow를 StateFlow로 변환할 수 있다.

// 1초마다 String값을 반환하는 Flow
val stringFlow: Flow<String> = flow {
    for (i in 0..1000) {
        emit("integer: $i")
        delay(1000)
    }
}
val stateFlow = stringFlow.stateIn(
    initialValue = "integer 0",
    started = SharingStarted.WhileSubscribed(5000),
    scope = viewModelScope
)
  • 초기값은 integer 0로 선언한다.
  • WhileSubscribed는 collector가 없어졌을때 지정 시간 이후 StateFlow 발행을 멈추도록하는 값이다. 5초뒤 동작을 멈추도록 하였다.
  • Viewmodel이 살아있는 동안 구독할 수 있도록 viewModelScope를 scope의 값으로 전달한다.

0개의 댓글

관련 채용 정보