StateFlow 和 SharedFlow 完全指南:在 Android 上超越 LiveData

  • 資料管理中冷流(Flow)和熱流(StateFlow 和 SharedFlow)之間存在根本差異。
  • 選擇持久狀態使用 StateFlow 還是短暫事件使用 SharedFlow 的標準。
  • 使用 repeatOnLifecycle 的安全收集策略來優化 UI 中的資源消耗。

StateFlow 和 SharedFlow:在 Android 平台上的效能優於 LiveData

如果你正在涉足 Android 開發領域,那麼你肯定遇到過如何處理表示層數據的這個老生常談的問題。長期以來,LiveData 一直佔據主導地位,但隨著 Kotlin 協程的出現,更強大、更靈活的工具應運而生。我們說的就是StateFlow 和 SharedFlow,它們本質上是響應式、有效率地管理資料流的自然演進。

事實上,雖然它們乍看之下似乎是相似的概念,但各自有著不同的用途。例如,讓螢幕記住新聞推送的上次顯示位置,與發送僅供查看一次的 Toast 通知是截然不同的。為了避免混淆,我們來詳細了解這些熱點流的工作原理,以及為什麼在現代專案中應該考慮放棄 LiveData。

流動的概念:冷還是熱?

要理解 StateFlow 和 SharedFlow,首先需要掌握冷流和熱流的差異。標準流程是冷流,就像食譜:在有人決定執行它(呼叫 `collect`)之前,什麼都不會發生。問題在於,如果你有三個訂閱者,生產者的程式碼就會執行三次,這會消耗大量電力或導致冗餘的 API 請求。

這就是熱流發揮作用的地方。 StateFlow 和 SharedFlow 都採用了策略性熱流機制,這意味著無論是否有人在監聽,生產者都會保持活躍狀態。這使得多個收集器可以共享同一個資料來源,避免應用程式重複執行相同的工作——這對保持系統流暢性至關重要。

StateFlow:LiveData 的理想替代方案

StateFlow 本質上是一個狀態容器。它的主要特點是始終維護一個當前值,該值可以透過 `.value` 屬性同步查詢。它是管理使用者介面狀態的理想工具,例如螢幕是否正在載入或個人資料資料是否已成功更新。

Android Studio 中多模組專案的配置與架構
相關文章:
Android Studio 多模組專案設定與結構化完整指南

與 LiveData 不同,StateFlow 的建構子需要一個初始值,避免了啟動時狀態為空的不確定性。此外,它還實現了資料合併和去重功能:如果連續兩次發出相同的值,流程會直接忽略它。這對於防止在資料實際上沒有變化時不必要地刷新 UI 非常有效。

要在 ViewModel 中實現這一點,最佳方法是使用私有的MutableStateFlow 回退屬性,並將其公開為不可變的 StateFlow。這樣可以確保只有 ViewModel 才能修改狀態,遵循單向資料流,並防止視圖操作資料。

SharedFlow:用於唯一操作的事件匯流排

如果 StateFlow 用於“狀態”,那麼 SharedFlow 用於“事件”。假設你想顯示一個錯誤對話框或導航到另一個畫面。如果使用 StateFlow,旋轉裝置會導致新的收集器接收到上次的值,事件會再次觸發—這是一個嚴重的 bug。 SharedFlow 解決了這個問題,因為它可以將重播值配置為零。

這種流程更加靈活,允許我們調整溢位緩衝區等參數。如果緩衝區已滿,我們可以選擇暫停發送方或丟棄最舊或最新的值。一個關鍵優勢是 SharedFlow允許您發送重複值;如果您需要發送三次相同的「連接錯誤」訊息,SharedFlow 會毫不猶豫地執行,而 StateFlow 則會將其過濾掉。

技術比較:StateFlow、SharedFlow 和 LiveData

為了避免在技術面試或架構設計中產生混淆,請務必考慮以下關鍵點。 LiveData 簡單易用,能夠自動管理生命週期,但它與 Android 平台緊密相關。 StateFlow 和 SharedFlow 作為 Kotlin 協程的一部分,具有平台無關性,這極大地簡化了單元測試和清晰的領域層建構。

  • StateFlow:強制設定初始值,具有 .value 屬性,對值進行去重,非常適合用於 UI 狀態。
  • 共享串流它不需要初始值,可以配置重播,是短暫事件的理想選擇。
  • LiveData它不需要初始值,它了解生命週期,但在非同步轉換方面功能較弱。

安全收割的重要性

一個非常常見的錯誤是在 Activity 的簡單協程作用域內執行 `collect` 方法。這很危險,因為即使應用在背景運行,流程也會繼續處理數據,這可能導致崩潰或 CPU 使用率過高。更現代的解決方案是使用`repeatOnLifecycle(Lifecycle.State.STARTED)` API 。

此函數會在視圖可見時啟動集合,並在集合達到 STOPPED 狀態時自動取消。對於 Jetpack Compose,建議使用`collectAsStateWithLifecycle()`,它原生整合了此邏輯,並確保應用程式有效利用裝置資源。

將冷流轉化為熱流

有時我們會收到來自資料庫(例如 Room)或儲存庫的資料流,但它是冷的。為了將其變為熱數據,我們使用`stateIn` 或 `shareIn`操作符。這些操作符需要一個 `CoroutineScope` 和一個啟動策略。最常用的是`SharingStarted.WhileSubscribed(5000)`。

這種配置非常巧妙,因為它能保證只要有訂閱者,視訊串流就會保持活躍;如果螢幕關閉,它會等待 5 秒鐘才會停止視訊串流。這一點至關重要,因為這樣在螢幕旋轉期間,視訊串流就不會被銷毀並重新創建,從而避免對伺服器或本地資料庫發出不必要的請求。

Android 中的 MVI 架構
相關文章:
Android MVI 架構完整指南

掌握這些工具的關鍵在於理解狀態必須持久化並使用 StateFlow 進行去重,而一次性操作則應透過 SharedFlow 處理。結合生命週期感知和使用 stateIn 進行冷流轉換,我們可以建立一個強大且完全響應式的 Android 架構,並針對行動裝置上的實際效能進行最佳化。分享本指南,幫助更多人了解這個主題。


Surfshark 安卓防毒軟體
它可能會讓你感興趣:
如何清除安卓手機病毒:完整且最新的手機清理指南
在 Google 中將其新增為首選來源