官方推薦 Flow 取代 LiveData,有必要嗎?,手撕面試官

mb612e277c48155 2021-09-18 03:34:43 阅读数:978

官方 flow 取代 livedata 必要

讓這些流持續活躍可能會引起不必要的資源浪費,例如一直通過從數據庫連接、硬件傳感器中讀取數據等等。當您的應用轉而在後臺運行時,您應當保持克制並中止這些協程。


public fun WhileSubscribed(
stopTimeoutMillis: Long = 0,
replayExpirationMillis: Long = Long.MAX_VALUE
)

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

如上所示,它支持兩個參數:

  • 1.stopTimeoutMillis 控制一個以毫秒為單比特的延遲值,指的是最後一個訂閱者結束訂閱與停止上遊流的時間差。默認值是 0 (立即停止).這個值非常有用,因為您可能並不想因為視圖有幾秒鐘不再監聽就結束上遊流。這種情况非常常見——比如當用戶旋轉設備時,原來的視圖會先被銷毀,然後數秒鐘內重建。

  • 2.replayExpirationMillis錶示數據重播的過時時間,如果用戶離開應用太久,此時您不想讓用戶看到陳舊的數據,你可以用到這個參數

4. StateFlow介紹


4.1 為什麼引入StateFlow

我們前面剛剛看了SharedFlow,為什麼又冒出個StateFlow?

StateFlowSharedFlow 的一個比較特殊的變種,StateFlowLiveData 是最接近的,因為:

  • 1.它始終是有值的。

  • 2.它的值是唯一的。

  • 3.它允許被多個觀察者共用 (因此是共享的數據流)。

  • 4.它永遠只會把最新的值重現給訂閱者,這與活躍觀察者的數量是無關的。

可以看出,StateFlowLiveData是比較接近的,可以獲取當前的值,可以想像之所以引入StateFlow就是為了替換LiveData

總結如下:

1.StateFlow繼承於SharedFlow,是SharedFlow的一個特殊變種

2.StateFlowLiveData比較相近,相信之所以推出就是為了替換LiveData

4.2 StateFlow的簡單使用

我們先來看看構造函數:


public fun <T> MutableStateFlow(value: T): MutableStateFlow<T> = StateFlowImpl(value ?: NULL)

  • 1.
  • 2.
  • 3.

1.StateFlow構造函數較為簡單,只需要傳入一個默認值

2.StateFlow本質上是一個replay為1,並且沒有緩沖區的SharedFlow,因此第一次訂閱時會先獲得默認值

3.StateFlow僅在值已更新,並且值發生了變化時才會返回,即如果更新後的值沒有變化,也沒會回調Collect方法,這點與LiveData不同

StateFlow類似,我們也可以用stateIn將普通流轉化成SharedFlow


val result: StateFlow<Result<UiState>> = someFlow
.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = Result.Loading
)

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

shareIn類似,唯一不同的時需要傳入一個默認值

同時之所以WhileSubscribed中傳入了5000,是為了實現等待5秒後仍然沒有訂閱者存在就終止協程的功能,這個方法有以下功能

  • 用戶將您的應用轉至後臺運行,5 秒鐘後所有來自其他層的數據更新會停止,這樣可以節省電量。

  • 最新的數據仍然會被緩存,所以當用戶切換回應用時,視圖立即就可以得到數據進行渲染。

  • 訂閱將被重啟,新數據會填充進來,當數據可用時更新視圖。

  • 在屏幕旋轉時,因為重新訂閱的時間在5s內,因此上遊流不會中止

4.3 在頁面中觀察StateFlow

LiveData類似,我們也需要經常在頁面中觀察StateFlow

觀察StateFlow需要在協程中,因此我們需要協程構建器,一般我們會使用下面幾種

  1. lifecycleScope.launch : 立即啟動協程,並且在本 ActivityFragment 銷毀時結束協程。

  2. LaunchWhenStartedLaunchWhenResumed,它會在lifecycleOwner進入X狀態之前一直等待,又在離開X狀態時掛起協程

官方推薦 Flow 取代 LiveData,有必要嗎?,手撕面試官_移動開發

如上圖所示:

1.使用launch是不安全的,在應用在後臺時也會接收數據更新,可能會導致應用崩潰

2.使用launchWhenStartedlaunchWhenResumed會好一些,在後臺時不會接收數據更新,但是,上遊數據流會在應用後臺運行期間保持活躍,因此可能浪費一定的資源

這麼說來,我們使用WhileSubscribed進行的配置豈不是無效了嗎?訂閱者一直存在,只有頁面關閉時才會取消訂閱

官方推薦repeatOnLifecycle來構建協程

在某個特定的狀態滿足時啟動協程,並且在生命周期所有者退出該狀態時停止協程,如下圖所示。

官方推薦 Flow 取代 LiveData,有必要嗎?,手撕面試官_Android_02

比如在某個Fragment的代碼中:


onCreateView(...) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(STARTED) {
myViewModel.myUiState.collect { ... }
}
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

當這個Fragment處於STARTED狀態時會開始收集流,並且在RESUMED狀態時保持收集,最終在Fragment進入STOPPED狀態時結束收集過程。

結合使用repeatOnLifecycle APIWhileSubscribed,可以幫助您的應用妥善利用設備資源的同時,發揮最佳性能

4.4 頁面中觀察Flow的最佳方式

通過ViewModel暴露數據,並在頁面中獲取的最佳方式是:

  • ?? 使用帶超時參數的 WhileSubscribed 策略暴露 Flow。[示例 1](

)

  • ?? 使用 repeatOnLifecycle 來收集數據更新。[示例 2](

)

官方推薦 Flow 取代 LiveData,有必要嗎?,手撕面試官_程序員_03

最佳實踐如上圖所示,如果采用其他方式,上遊數據流會被一直保持活躍,導致資源浪費

當然,如果您並不需要使用到Kotlin Flow的强大功能,就用LiveData好了

5 StateFlowSharedFlow有什麼區別?


從上文其實可以看出,StateFlowSharedFlow其實是挺像的,讓人有些傻傻分不清,有時候也挺難選擇該用哪個的

我們總結一下,它們的區別如下:

  1. SharedFlow配置更為靈活,支持配置replay,緩沖區大小等,StateFlowSharedFlow的特化版本,replay固定為1,緩沖區大小默認為0

  2. StateFlowLiveData類似,支持通過myFlow.value獲取當前狀態,如果有這個需求,必須使用StateFlow

  3. SharedFlow支持發出和收集重複值,而StateFlowvalue重複時,不會回調collect

  4. 對於新的訂閱者,StateFlow只會重播當前最新值,SharedFlow可配置重播元素個數(默認為0,即不重播)

可以看出,StateFlow為我們做了一些默認的配置,在SharedFlow上添加了一些默認約束,這些配置可能並不符合我們的要求

  1. 它忽略重複的值,並且是不可配置的。這會帶來一些問題,比如當往List中添加元素並更新時,StateFlow會認為是重複的值並忽略

  2. 它需要一個初始值,並且在開始訂閱時會回調初始值,這有可能不是我們想要的

  3. 它默認是粘性的,新用戶訂閱會獲得當前的最新值,而且是不可配置的,而SharedFlow可以修改replay

StateFlow施加在SharedFlow上的約束可能不是最適合您,如果不需要訪問myFlow.value,並且享受SharedFlow的靈活性,可以選擇考慮使用SharedFlow

總結

簡單往往意味著不够强大,而强大又常常意味著複雜,兩者往往不能兼得,軟件開發過程中常常面臨這種取舍。

LiveData的簡單並不是它的缺點,而是它的特點。StateFlowSharedFlow更加强大,但是學習成本也顯著的更高.

我們應該根據自己的需求合理選擇組件的使用

  1. 如果你的數據流比較簡單,不需要進行線程切換與複雜的數據變換,LiveData對你來說相信已經足够了

  2. 如果你的數據流比較複雜,需要切換線程等操作,不需要發送重複值,需要獲取myFlow.valueStateFlow對你來說是個好的選擇

  3. 如果你的數據流比較複雜,同時不需要獲取myFlow.value,需要配置新用戶訂閱重播無素的個數,或者需要發送重複的值,可以考慮使用SharedFlow

參考資料

[Google 推薦在 MVVM 架構中使用 Kotlin Flow](

)

[Migrate from LiveData to StateFlow and SharedFlow](

)

[從 LiveData 遷移到 Kotlin 數據流](

)

[關於kotlin中的Collections、Sequence、Channel和Flow (二)](

)

其他資料

最後

在這裏我和身邊一些朋友特意整理了一份快速進階為Android高級工程師的系統且全面的學習資料。涵蓋了Android初級——Android高級架構師進階必備的一些學習技能。

附上:我們之前因為秋招收集的二十套一二線互聯網公司Android面試真題(含BAT、小米、華為、美團、滴滴)和我自己整理Android複習筆記(包含Android基礎知識點、Android擴展知識點、Android源碼解析、設計模式匯總、Gradle知識點、常見算法題匯總。)

官方推薦 Flow 取代 LiveData,有必要嗎?,手撕面試官_Android_04

本文在 CodeChina開源項目:《Android學習筆記總結+移動架構視頻+大廠面試真題+項目實戰源碼》中已收錄,裏面包含不同方向的自學編程路線、面試題集合/面經、及系列技術文章等

版权声明:本文为[mb612e277c48155]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/09/20210918033443040l.html