1.4.0協程之StateFlow和SharedFlow介紹

kotliner發表於2020-11-02

今天,我們很高興地釋出Kotlin Coroutines庫的1.4.0版。該版本重點關注已穩定的StateFlowSharedFlow API。當Kotlin Coroutines非同步執行的上下文中需要狀態管理,StateFlow和SharedFlow便是被設計用於這種場景。

img

Kotlin中的Flow API旨在非同步處理按順序執行的資料流。Flow本質上是一個Sequence。我們可以像對Kotlin中Sequence一樣來操作Flow:變換,過濾,對映等。Kotlin SequencesFlow的主要區別在於Flow可以掛起。

Flow中,你可以在任何地方掛起:例如builder函式中,或Flow API提供的運算子中。 Flow的掛起行為就像受背壓控制一樣,但是不需要你做任何事——編譯器將完成所有工作。

Flow就像使用Kotlin Sequences一樣簡單。但提供了所有響應式程式設計的優點,而無需管理背壓。

Flow是一套方便的API,但它不提供部分場景所必需的狀態管理。例如,一個流程可能具有多箇中間狀態和一個終止狀態。下載檔案就是這類流程的一個示例:下載持續的時間中,中間狀態可能會從“開始”過渡到“下載中”標記,然後終止狀態是“成功”或“失敗” 。在這種情況下,無論下載是否成功,我們只對結果感興趣。

使用Flow API實施上述方案時,我們希望狀態的變動能通知到會有所動作的觀察者。借鑑歷史,我們建議使用ConflatedBroadcastChannel。但是,ConflatedBroadcastChannel對於手頭的任務來說有點太複雜了。另外,使用通道進行狀態管理時會出現一些邏輯上的不一致。例如,可以關閉或取消通道。但由於無法取消狀態,因此在狀態管理中無法正常使用!

我們已決定棄用ConflatedBroadcastChannel,取而代之引入一對新的API——StateFlowSharedFlow

StateFlow

StateFlow有兩種型別: StateFlowMutableStateFlow:

狀態由其值表示。任何對值的更新都會反饋新值到所有流的接收器中。

讓我們看一下如何使用新的API來實現我們前面描述的檔案下載示例。

本示例向客戶端暴露了* state的不可變版本,並在內部管理可變狀態(state*)。在下載函式中,我們先初始化了內部狀態值:** state.value = DownloadStatus.INITIALIZED*。然後透過進度指示數字來更新內部狀態。最後我們使用終止值(指代下載狀態)更新 state *。

如你所見,沒有channel API。我們沒有釋出額外的協程,也不需要學習新的概念。只是依賴變數實現的簡單命令式程式碼,並向客戶端暴露了Flow型別的* state*。

SharedFlow

如果只是需要管理一系列狀態更新(即事件流),而非管理狀態,該怎麼辦?對於這個用例,我們有一套名為SharedFlow的新API。如果你對發出的一連串值感興趣,則這API十分方便。例如,根據資料流計算滑動平均值。

共享流只是一個流,其中包含可用作原子快照的replay cache。每個新的訂閱者會先從replay cache中獲取值,然後才收到新發出的值。

除了SharedFlow,我們也提供了MutableSharedFlow

MutableSharedFlow可用於從掛起或非掛起的上下文中發射值。顧名思義,可以重置MutableSharedFlow的replay cache。而且還將訂閱者的數量作為Flow暴露出來。

實現自己的MutableSharedFlow可能很麻煩。因此,我們提供了一些使用SharedFlow的便捷方法。

要使用上面的函式初始化MutableSharedFlow例項,我們可以指定重播給新訂閱者的值的數量,緩衝區容量以及緩衝區已滿時該怎麼辦。例如,如果緩衝區已滿,我們可以選擇掛起流。

如果你已經有一個Flow例項,並且希望使其能被共享,則可以使用新的運算子Flow.shareIn

總結

新的StateFlow和SharedFlow API提供了在Kotlin程式中更優雅使用協程管理狀態的方案。和使用broadcast channel從流上下文中釋出狀態變更相比,它們更加簡單和直觀。

StateFlow和SharedFlow目前還處於實驗階段。請嘗試、測試、破壞,然後將你的反饋分享給我們!讓新的API穩定是我們的首要任務之一。

更多相關的詳細資訊,及Kotlin Coroutines的新功能,請觀看Kotlin 1.4線上活動中Vsevolod Tolstopyatov的演講。

相關文章