iT邦幫忙

2021 iThome 鐵人賽

DAY 25
1
Software Development

Coroutine 停看聽系列 第 25

Day 25:[Android] 將 LiveData 用 Flow 替代吧

LiveData 是 Android 中一個很有用的項目,它是一種可觀察(Observe)的資料存儲器類(data holder)。它會感知 Android 的生命週期,也就是說當 Activity 或是 Fragment 在 STARTEDRESUMED 時,所被觀察的資料才會是在啟動狀態(active state)的。

在 MVVM 架構中,我們將架構分成 Model - ViewModel - View,而 LiveData 就是負責在 ViewModel 與 View 中的資料傳遞。而 LiveData 存放的則是 UI 介面上所需要的資料,所以它對於資料的更新需要具有敏感度,當 LiveData 內的資料更新時,View 層應該就要能夠立刻更新,減少手動呼叫。

View 層透過觀察 ViewModel 層暴露出的 LiveData,當資料變更的時候,就會自動通知 View 層更新畫面。

使用方法如下:

  • ViewModel 層

先定義 ViewModel 層,這一層主要是要決定哪些資料要暴露給 View 層,以及要這些資料要從哪邊取得,所以 ViewModel 是介於 View 與 Model 的。

class MyViewModel : ViewModel() {
    private var _data = MutableLiveData<List<Driver>>()
    val data: LiveData<List<Driver>> = _data
		...

		init{
			val result = ...
			_data.value = result
		}
}
  • View 層
class MyFragment: Fragment(){
		...
		override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
		        super.onViewCreated(view, savedInstanceState)
			viewModel.data.observe(viewLifecycleOwner, {
		         setText(it)
			})
		}
		...
}

這邊我們就完成了一個簡單的 LiveData 。回到我們的主題 Flow,我們有沒有可能使用 Flow 來替代 LiveData 呢?

首先,我們先來分析一下 LiveData。 LiveData 可以讓 View 層「觀察」,等到資料更新時,就會把更新的資料通知給 View 層,所以在 View 層就不需要主動去查詢現在的值是什麼,只需要靜靜的等待即可。Observe and forget。

我們複習一下兩種 Flow 的用法,Shared Flow 可以與多個訂閱者分享資料,當有新的訂閱者加入時,會把最後面的幾筆資料傳給新訂閱者。之後每次有新的資料的時候,就會同時發送給所有訂閱者。而 State Flow 則是 Shared Flow 的特例,它每次建立的時候一定會有值,而在每次有新訂閱者加入時,只會把最後一筆的資料發送給新訂閱者,因為 StateFlow 只會儲存一個值。

這邊我們選擇 StateFlow 來作為 LiveData 的替代品。

StateFlow 替代 LiveData

將上面的 ViewModel 改用 StateFlow 替代

class MyViewModel : ViewModel() {
    private var _data = MutableStateFlow<List<Driver>>(emptyList())
    val data = _data.asStateFlow()
		...

		init{
			val result = ...
			_data.value = result
		}
}
class MyFragment: Fragment(){
		private var updatesJob: Job? = null
		...
		override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
		        super.onViewCreated(view, savedInstanceState)
				updatesJob = lifecycleScope.launch {
            viewModel._data.collect {
                setText()
            }
        }
		}
		...

		override fun onDestroy() {
        super.onDestroy()
        updatesJob?.cancel()
    }
}

可以發現,我們除了將 LiveData 用 Flow 取代之外,我們在 View 層還必須要使用 lifecycleScope.launch 來把 StateFlow 包裝起來,因為 collect 是一個 suspend 函式。

為了避免在離開的時候還繼續收資料,必須要在離開畫面的時候把 Job 給停掉。

有點麻煩

不過在 lifecycle:lifecycle-runtime-ktx:2.4.0 會改,但是目前還是只有 alpha 版而已。

小結

Flow 能夠替代 LiveData 是肯定的,不過因為 LiveData 非常簡單使用,所以以目前來說不一定需要使用 Flow 來替代 LiveData 。畢竟 LiveData 與 Activity/Fragment 的生命週期綁在一起,當畫面結束的時候,訂閱的資料也就不會繼續更新。如果以目前的 Flow 來說,首先要思考的是我們在 View 層就需要建立一個 Scope 讓這個 Flow 跑在背景,等到結束畫面的時候,還要記得把它停掉。這樣子很容易就出錯了。

或許我們可以等到更穩定的時候,Flow 像現在的 LiveData 那麼實用的時候才來改。

參考資料

Migrating from LiveData to Kotlin’s Flow

A safer way to collect flows from Android UIs

特別感謝

Kotlin Taiwan User Group

Kotlin 讀書會


上一篇
Day24:Hot flow - State Flow (part II)
下一篇
Day26:Flow 的運算子 - buffer()
系列文
Coroutine 停看聽30

尚未有邦友留言

立即登入留言