StateFlow 和 SharedFlow 是資料流 API,能讓資料流以最佳方式發出狀態更新,並向多個取用端發出值。
StateFlow
StateFlow敬上 
是狀態容器的可觀測資料流,可發出目前狀態和新的狀態
對收集器進行更新。目前狀態值也可以透過
value敬上 
資源。如要更新狀態並傳送至資料流,請將新的值指派給 MutableStateFlow 類別的 value 屬性。
在 Android 中,StateFlow 非常適合需要維持可觀測、可變動狀態的類別。
按照 Kotlin 資料流的範例,StateFlow 可以從 LatestNewsViewModel 公開,以便 View 監聽 UI 狀態更新,並且使螢幕狀態在設定變更後自行繼續留存。
class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {
    // Backing property to avoid state updates from other classes
    private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList()))
    // The UI collects from this StateFlow to get its state updates
    val uiState: StateFlow<LatestNewsUiState> = _uiState
    init {
        viewModelScope.launch {
            newsRepository.favoriteLatestNews
                // Update View with the latest favorite news
                // Writes to the value property of MutableStateFlow,
                // adding a new element to the flow and updating all
                // of its collectors
                .collect { favoriteNews ->
                    _uiState.value = LatestNewsUiState.Success(favoriteNews)
                }
        }
    }
}
// Represents different states for the LatestNews screen
sealed class LatestNewsUiState {
    data class Success(val news: List<ArticleHeadline>): LatestNewsUiState()
    data class Error(val exception: Throwable): LatestNewsUiState()
}
負責更新 MutableStateFlow 的類別為生產端,而從 StateFlow 收集的所有類別為取用端。與使用 flow 建構工具建構的「冷」資料流不同,StateFlow 是「熱」資料流:從資料流收集,不會觸發任何生產端程式碼。StateFlow 會一直處於啟用中狀態,且會留在記憶體中,但僅當垃圾收集的根層級沒有其他對該資料流的參照時,才可用於垃圾收集。
當新的取用端開始從資料流收集資訊時,它就會收到串流中的最後一個狀態和任何後續狀態。您可以在其他可觀測的類別中發現這種行為,例如 LiveData。
就像任何其他資料流一樣,View 會監聽 StateFlow:
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)
                    }
                }
            }
        }
    }
}
如要將任何資料流轉換為 StateFlow,請使用
stateIn 
中繼運算子。
StateFlow、資料流和 LiveData
StateFlow 和 LiveData 有相似之處。兩者都是可觀測的資料容器類別,且在應用程式架構中使用時,都會遵循類似的模式。
但請注意,StateFlow 和 LiveData 的執行方式不同:
- StateFlow需要將初始狀態傳遞到建構函式,- LiveData卻不需要。
- 當檢視畫面進入 STOPPED狀態時,LiveData.observe()會自動取消註冊取用端,而從StateFlow或任何其他資料流收集的作業不會自動停止。為了達成 行為,您需要從Lifecycle.repeatOnLifecycle收集資料流 封鎖。
使用 shareIn 讓冷資料流變熱
StateFlow 是「熱」資料流;只要收集該資料流,或垃圾收集的根層級存有對該資料流的任何其他參照,它就會留存在記憶體中。您可以使用
shareIn敬上 
運算子。
以 Kotlin 資料流中建立的 callbackFlow 為例,只要使用 shareIn,即可在收集器之間共用從 Firestore 擷取的資料,而不必讓每個收集器建立新資料流。您需要傳入以下項目:
- 用來共用資料流的 CoroutineScope。這個範圍應比任何取用端留存更久,才可視需要使共用的資料流繼續留存。
- 向每個新收集器重播的項目數量。
- 啟動行為政策。
class NewsRemoteDataSource(...,
    private val externalScope: CoroutineScope,
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
}
在這個範例中,latestNews 資料流會向新收集器重送最後一個發出的項目,且只要 externalScope 繼續留存,並有啟用中的收集器,就會保持啟用中狀態。當有活躍的訂閱者時,SharingStarted.WhileSubscribed() 啟動政策會使上游生產端保持啟用中狀態。您也可以使用其他啟動政策,例如使用 SharingStarted.Eagerly 即可立即啟動生產端;如果使用 SharingStarted.Lazily,即可在第一個訂閱者出現後開始共用資料,並使資料流永久保持啟用狀態。
SharedFlow
shareIn 函式會傳回 SharedFlow。後者是熱資料流,會向從其收集資料的所有取用端發送值。SharedFlow 是 StateFlow 的可高度設定一般化功能。
即使不使用 shareIn,您仍可建立 SharedFlow。例如,您可以使用 SharedFlow 將滴答傳送至應用程式的其餘部分,這樣所有內容就會同時定期重新整理。除了擷取最新消息外,建議您一併重新整理使用者資訊部分,附上最愛的主題集合。在以下程式碼片段中,TickHandler 會公開 SharedFlow,方便其他類別知道重新整理內容的時間。和 StateFlow 一樣,請使用類別中 MutableSharedFlow 類型的支援屬性將項目傳送至資料流:
// Class that centralizes when the content of the app needs to be refreshed
class TickHandler(
    private val externalScope: CoroutineScope,
    private val tickIntervalMs: Long = 5000
) {
    // Backing property to avoid flow emissions from other classes
    private val _tickFlow = MutableSharedFlow<Unit>(replay = 0)
    val tickFlow: SharedFlow<Event<String>> = _tickFlow
    init {
        externalScope.launch {
            while(true) {
                _tickFlow.emit(Unit)
                delay(tickIntervalMs)
            }
        }
    }
}
class NewsRepository(
    ...,
    private val tickHandler: TickHandler,
    private val externalScope: CoroutineScope
) {
    init {
        externalScope.launch {
            // Listen for tick updates
            tickHandler.tickFlow.collect {
                refreshLatestNews()
            }
        }
    }
    suspend fun refreshLatestNews() { ... }
    ...
}
您可以透過下列方式自訂 SharedFlow 行為:
- replay可讓您重新傳送許多先前為訂閱者發出的值。
- onBufferOverflow可指定用於規定緩衝區何時充滿要傳送的項目的政策。預設值為- BufferOverflow.SUSPEND,可使呼叫端暫停。其他選項為- DROP_LATEST或- DROP_OLDEST。
MutableSharedFlow 也具有 subscriptionCount 屬性,其中會指出有多少個啟用中的收集器,讓您可視情況將商業邏輯最佳化。如果不想重播傳送至資料流的最新資訊,也可使用 MutableSharedFlow 的 resetReplayCache 函式。
其他資料流資源
- Android 上的 Kotlin 資料流
- 在 Android 上測試 Kotlin 資料流
- 資料流的 shareIn 和 StateIn 運算子必學知識
- 從 LiveData 遷移至 Kotlin 資料流
- Kotlin 協同程式和資料流的其他資源
