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 读书会


<<:  Day16:今天来聊一下如何使用njRAT RAT Trojan控制Windows电脑攻防

>>:  Day30. Blue Prism本届最终章 –BP幕後花絮

[Android Studio] -- Day 3 Activity练习

前言 今天将针对activity的跳转来复习复习 正文 这次采用bundle来传值,并区分start...

Day06字体样式(HTML)

字体样式 今天来介绍几个设计字体样式的标签 有了昨天使用标签的概念 今天就可以直接把这些效果套用在句...

[Day23]ISO 27001 附录 A.11 实体及环境安全

这个章节的重点在於资讯管理系统的实体环境的保护。 不是这种保护 XD 不过,比较常跟受稽方讨论到乖乖...

伸缩自如的Flask [day4] JWT

好的,你很辛苦的写了很多API function,但是你却不希望闲杂人等没事就call一下你的API...

Day 05 - 想要够给力的机器-EC2

来到了中秋连假的第一天,买不到云上的月亮,我们就到云上买台机器来玩玩吧 1. 使用EC2好处? EC...