[TIL-230623] StateFlow 알아보기

반응형

안녕하세요

날씨가 많이 더워지고 있네요.. 덥다보니 축 처지게 되는데.. 학습을 게을리 하지말고
하루하루 배우는 시간이 되게 해야겠습니다


오늘은 Android에서 사용되는 StateFlow에 대해 포스팅하려 합니다.
StateFlow에 대해 학습하려면, Flow에 대해 먼저 하시면 이해에 도움이 되실 것 같습니다.


출처 : https://www.youtube.com/watch?v=SP_btJHXqN8

 

StateFlow란?

StateFlow란

  • 특정 State를 유지, 관찰
  • Collector에 현재와 새로운 State를 업데이트
  • 현재 State 값은 StateFlow.value로 읽을 수 있습니다.

 

class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {

    // 다른 클래스의 상태 업데이트를 방지하기 위한 백업 속성
    private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList()))
    // UI는 상태 업데이트를 위해 StateFlow를 수집합니다.
    val uiState: StateFlow<LatestNewsUiState> = _uiState

    init {
        viewModelScope.launch {
            newsRepository.favoriteLatestNews
                // 최신 인기 뉴스로 보기 업데이트
                // MutableStateFlow의 value 속성에 기록하여 흐름에 새 요소를 추가하고 모든 수집기를 업데이트합니다.
                .collect { favoriteNews ->
                    _uiState.value = LatestNewsUiState.Success(favoriteNews)
                }
        }
    }
}

// LatestNews 화면에 다양한 상태를 나타냅니다.
sealed class LatestNewsUiState {
    data class Success(val news: List<ArticleHeadline>): LatestNewsUiState()
    data class Error(val exception: Throwable): LatestNewsUiState()
}

 

  • StateFlow는 LatestNewsViewModel에서 출력될 수 있습니다.
  • 따라서, View 상태 업데이트를 수신할 수 있고, 기본적으로 화면 상태가 구성 변경을 유지하도록 합니다.
  • MutableStateFlow의 업데이트를 담당하는 클래스가 Producer 입니다.
  • StateFlow를 수집하는 모든 클래스들은 Consumer 입니다.
  • flow 빌더를 사용하여 구축하는 cold flow 와는 달리, StateFlow는 Flow에서 수집하여도 Producer가 트리거되지 않습니다. > hot flow
  • StateFlow는 항상 Active 상태이고 메모리에 있습니다.
  • 가비지 컬렉션 루트에서 이에 대한 다른 참조가 없는 경우 가비지 컬렉션 대상이 됩니다.


  • 새로운 Customer가 Flow로부터 수집을 시작할 때, 스트림의 마지막 상태와 후속 상태를 전달받습니다.
  • View는 다른 Flow와 마찬가지로 StateFlow를 수신합니다.
class LatestNewsActivity : AppCompatActivity() {
    private val latestNewsViewModel = // getViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        //수명 주기 범위에서 Coroutine을 시작합니다.
        lifecycleScope.launch {
            // repeatOnLifecycle 수명 주기가 STARTED 상태에 있을 때마다
            // 새 Coroutine에서 블록을 시작하고 STOPPED 일 때 취소합니다.
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Flow를 트리거하고, 값을 수신합니다.
                // 수명 주기가 STARTED일 때 발생하고, STOPPED일 때 중지됩니다.
                latestNewsViewModel.uiState.collect { uiState ->
                    // 새로운 값 수신
                    when (uiState) {
                        is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
                        is LatestNewsUiState.Error -> showError(uiState.exception)
                    }
                }
            }
        }
    }
}

 

  • UI를 업데이트 해야 하는 경우, launch (또는 launchIn) 확장 기능에서 직접 UI에서 Flow를 수집하면 안됩니다.
  • View가 보이지 않는 경우에도 이벤트를 처리합니다.
  • 이 동작은 앱 충돌로 이루어 질 수도 있습니다.

  • 임의의 flow를 StateFlow를 변환하기 위해서, stateIn 을 사용합니다.

 

StateFlow, Flow, LIveData
  • 공통점
    • StateFlow와 LiveData는 서로 유사합니다.
    • 두 관찰가능한 데이터 홀더 클래스
    • 앱 아키텍처에 사용될 때 간단한 패턴을 따릅니다.
  • 다른점
    • StateFlow는 생성자에 초기 상태를  요청하지만, LiveData는 해당 기능이 없습니다.
    • LiveData.observe()는 view가 STOPPED 상태가 되면 자동으로 Consumer 등록을 취소합니다.
    • StateFlow나 다른 flow를 수집해도 자동 중지는 되지 않습니다.
    • LiveData로 유사한 기능을 구현하려면, Lifecycle.repeatOnLifecycle 블록에서 Flow를 수집합니다.

 

 

shareIn을 사용하여 cold flow를 hot으로 만들기
  • 새 Flow를 생성하는 대신 shareIn을 사용하여 수집기 간에 Firestore에서 검색된 데이터를 공유할 수 있습니다.
class NewsRemoteDataSource(...,
    private val externalScope: CoroutineScope,
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
}

 

  • 전달해야하는 것들
    • CoroutineScope : Flow를 공유하는데 사용됩니다.
      • 범위는 공유된 Flow가 필요한 만큼 오래 유지하기 위해 Customer 보다 오래 있어야 합니다.
    • 각각의 새 수집기에 재생할 항목 수
    • 시작 동작 정책


오늘은 StateFlow에 대해서 알아보았습니다.

반응형