Android Flow 与Live Data对比

计算机软件开发 2024-9-3 02:59:41 101 0 来自 中国
[转]官方保举 Flow 代替 LiveData,有必要吗?
更加具体的文章:不做跟风党,LiveData,StateFlow,SharedFlow 的利用场景对比
媒介

打开Android架构组件页面,我们可以发现一些最新发布的jetpack组件,如Room,DataStore, Paging3,DataBinding 等都支持了Flow
Google开辟者账号近来也发布了几篇利用Flow的文章,好比:从 LiveData 迁移到 Kotlin 数据流
看起来官方在大力保举利用Flow代替LiveData,那么题目来了,有必要吗?
我LiveData用得好好的,有必要再学Flow吗?本文重要答复这个题目,具体包罗以下内容
1.LiveData有什么不敷?
2.Flow先容以及为什么会有Flow
3.SharedFlow与StateFlow的先容与它们之间的区别
本文具体目次如下所示:

1. LiveData有什么不敷?

要相识LiveData的不敷,我们先相识下LiveData为什么被引入
LiveData 的汗青要追溯到 2017 年。彼时,观察者模式有用简化了开辟,但诸如 RxJava 一类的库对新手而言有些太过复杂。为此,架构组件团队打造了LiveData: 一个专用于 Android 的具备自主生命周期感知本领的可观察的数据存储器类。LiveData 被故意简化筹划,这使得开辟者很轻易上手;而对于较为复杂的交互数据流场景,发起您利用 RxJava,这样两者连合的上风就发挥出来了
可以看出,LiveData就是一个简朴易用的,具备感知生命周期本领的观察者模式
它利用起来非常简朴,这是它的优点,也是它的不敷,由于它面临比力复杂的交互数据流场景时,处理惩罚起来比力麻烦
1.2 LiveData的不敷

我们上文说过LiveData结构简朴,但是不敷强大,它有以下不敷

  • LiveData只能在主线程更新数据
  • LiveData的操纵符不敷强大,在处理惩罚复杂数据流时有些左支右绌
关于LiveData只能在主线程更新数据,有的同砚可能要问,不是有postValue吗?着实postValue也是必要切换到到主线程的,如下图所示:


这意味着当我们想要更新LiveData对象时,我们会常常更改线程(工作线程→主线程),假如在修改LiveData后又要切换回到工作线程那就更麻烦了,同时postValue可能会有丢数据的题目。
2. Flow先容

Flow 就是 Kotlin 协程与响应式编程模子连合的产物,你会发现它与 RxJava 非常像,二者之间也有相互转换的 API,利用起来非常方便。
2.1 为什么引入Flow

为什么引入Flow,我们可以从Flow办理了什么题目标角度切入

  • LiveData不支持线程切换,全部数据转换都将在主线程上完成,偶然必要频仍更改线程,面临复杂数据流时处理惩罚起来比力麻烦
  • 而RxJava又有些过于麻烦了,有很多让人傻傻分不清的操纵符,入门门槛较高,同时必要本身处理惩罚生命周期,在生命周期竣事时取消订阅
可以看出,Flow是介于LiveData与RxJava之间的一个办理方案,它有以下特点

  • Flow 支持线程切换、背压
  • Flow 入门的门槛很低,没有那么多傻傻分不清楚的操纵符
  • 简朴的数据转换与操纵符,如 map 等等
  • 冷数据流,不消耗则不生产数据,这一点与LiveData差别:LiveData的发送端并不依赖于吸收端。
  • 属于kotlin协程的一部门,可以很好的与协程底子办法连合
关于Flow的利用,比力简朴,有爱好的同砚可参阅文档:Flow文档
3. SharedFlow先容

我们上面先容过,Flow 是冷流,什么是冷流?

  • 冷流 :只有订阅者订阅时,才开始实行发射数据流的代码。而且冷流和订阅者只能是一对一的关系,当有多个差别的订阅者时,消息是重新完备发送的。也就是说对冷流而言,有多个订阅者的时间,他们各自的变乱是独立的。
  • 热流:无论有没有订阅者订阅,变乱始终都会发生。当 热流有多个订阅者时,热流与订阅者们的关系是一对多的关系,可以与多个订阅者共享信息。
3.1 为什么引入SharedFlow

上面着实已经说得很清楚了,冷流和订阅者只能是一对一的关系,当我们要实现一个流,多个订阅者的需求时(这在开辟中是很常见的),就必要热流了
从定名上也很轻易明白,SharedFlow即共享的Flow,可以实现一对多关系,SharedFlow是一种热流
3.2 SharedFlow的利用

我们来看看SharedFlow的构造函数
public fun <T> MutableSharedFlow(    replay: Int = 0,    extraBufferCapacity: Int = 0,    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND): MutableSharedFlow<T>其重要有3个参数
1.replay表现当新的订阅者Collect时,发送几个已经发送过的数据给它,默以为0,即默认新订阅者不会获取从前的数据
2.extraBufferCapacity表现减去replay,MutableSharedFlow还缓存多少数据,默以为0
3.onBufferOverflow表现缓存战略,即缓冲区满了之后Flow怎样处理惩罚,默以为挂起
简朴利用如下
//ViewModelval sharedFlow=MutableSharedFlow<String>()viewModelScope.launch{      sharedFlow.emit("Hello")      sharedFlow.emit("SharedFlow")}//ActivitylifecycleScope.launch{    viewMode.sharedFlow.collect {        print(it)    }}3.3 将冷流转化为SharedFlow

平凡flow可利用shareIn扩展方法,转化成SharedFlow
    val sharedFlow by lazy {        flow<Int> {        //...        }.shareIn(viewModelScope, WhileSubscribed(500), 0)    }shareIn重要也有三个参数:
@param scope 共享开始时地点的协程作用域范围
@param started 控制共享的开始和竣事的战略
@param replay 状态流的重播个数
started 担当以下的三个值:

  • Lazily: 当首个订阅者出现时开始,在scope指定的作用域被竣事时制止。
  • Eagerly: 立刻开始,而在scope指定的作用域被竣事时制止。
  • WhileSubscribed: 这种环境有些复杂,后面会具体解说
对于那些只实行一次的操纵,您可以利用Lazily大概Eagerly。然而,假如您必要观察其他的流,就应该利用WhileSubscribed来实现渺小但又告急的优化工作
3.4 Whilesubscribed战略

WhileSubscribed战略会在没有网络器的环境下取消上游数据流,通过shareIn运算符创建的SharedFlow会把数据暴露给视图 (View),同时也会观察来自其他层级大概是上游应用的数据流。
让这些流连续活泼可能会引起不必要的资源浪费,比方不绝通过从数据库连接、硬件传感器中读取数据等等。当您的应用转而在背景运行时,您应当保持克制并制止这些协程。
public fun WhileSubscribed(   stopTimeoutMillis: Long = 0,   replayExpirationMillis: Long = Long.MAX_VALUE)如上所示,它支持两个参数:

  • stopTimeoutMillis 控制一个以毫秒为单元的延长值,指的是末了一个订阅者竣事订阅与制止上游流的时间差。默认值是 0 (立刻制止).这个值非常有用,由于您可能并不想由于视图有几秒钟不再监听就竣事上游流。这种环境非常常见——好比当用户旋转装备时,原来的视图会先被烧毁,然后数秒钟内重修。
  • replayExpirationMillis表现数据重播的逾期时间,假如用户离开应用太久,此时您不想让用户看到陈旧的数据,你可以用到这个参数
4. StateFlow先容

4.1 为什么引入StateFlow

我们前面刚刚看了SharedFlow,为什么又冒出个StateFlow?
StateFlow 是 SharedFlow 的一个比力特别的变种,StateFlow与 LiveData 是最靠近的,由于:

  • 它始终是有值的。
  • 它的值是唯一的。
  • 它答应被多个观察者共用 (因此是共享的数据流)。
  • 它永久只会把最新的值重现给订阅者,这与活泼观察者的数量是无关的。
可以看出,StateFlow与LiveData是比力靠近的,可以获取当前的值,可以想像之所以引入StateFlow就是为了更换LiveData

  • StateFlow继承于SharedFlow,是SharedFlow的一个特别变种
  • StateFlow与LiveData比力相近,信托之所以推出就是为了更换LiveData
4.2 StateFlow的简朴利用

我们先来看看构造函数:
public fun <T> MutableStateFlow(value: T): MutableStateFlow<T> = StateFlowImpl(value ?: NULL)

  • StateFlow构造函数较为简朴,只必要传入一个默认值
  • StateFlow本质上是一个replay为1,而且没有缓冲区的SharedFlow,因此第一次订阅时会先得到默认值
  • StateFlow仅在值已更新,而且值发生了变革时才会返回,即假如更新后的值没有变革,也没会回调Collect方法,这点与LiveData差别
与SharedFlow类似,我们也可以用stateIn将平凡流转化成StateFlow
val result: StateFlow<Result<UiState>> = someFlow    .stateIn(        scope = viewModelScope,         started = WhileSubscribed(5000),         initialValue = Result.Loading    )与shareIn类似,唯一差别的时必要传入一个默认值
同时之所以WhileSubscribed中传入了5000,是为了实现等候5秒后仍旧没有订阅者存在就制止协程的功能,这个方法有以下功能

  • 用户将您的应用转至背景运行,5 秒钟后全部来自其他层的数据更新会制止,这样可以节流电量。
  • 最新的数据仍旧会被缓存,所以当用户切换回应用时,视图立刻就可以得到数据举行渲染。
  • 订阅将被重启,新数据会添补进来,当数据可用时更新视图。
  • 在屏幕旋转时,由于重新订阅的时间在5s内,因此上游流不会制止
4.3 在页面中观察StateFlow

与LiveData类似,我们也必要常常在页面中观察StateFlow
观察StateFlow必要在协程中,因此我们必要协程构建器,一样寻常我们会利用下面几种

  • lifecycleScope.launch : 立刻启动协程,而且在本 Activity或Fragment 烧毁时竣事协程。
  • LaunchWhenStarted 和 LaunchWhenResumed,它会在lifecycleOwner进入X状态之前不绝等候,又在离开X状态时挂起协程

如上图所示:
1.利用launch是不安全的,在应用在背景时也会吸收数据更新,可能会导致应用崩溃
2.利用launchWhenStarted或launchWhenResumed会好一些,在背景时不会吸收数据更新,但是,上游数据流会在应用背景运行期间保持活泼,因此可能浪费一定的资源
这么说来,我们利用WhileSubscribed举行的设置岂不是无效了吗?订阅者不绝存在,只有页面关闭时才会取消订阅
官方保举repeatOnLifecycle来构建协程
在某个特定的状态满足时启动协程,而且在生命周期全部者退出该状态时制止协程,如下图所示。


好比在某个Fragment的代码中:
onCreateView(...) {    viewLifecycleOwner.lifecycleScope.launch {        viewLifecycleOwner.lifecycle.repeatOnLifecycle(STARTED) {            myViewModel.myUiState.collect { ... }        }    }}当这个Fragment处于STARTED状态时会开始网络流,而且在RESUMED状态时保持网络,终极在Fragment进入STOPPED状态时竣事网络过程。
连合利用repeatOnLifecycle API和WhileSubscribed,可以资助您的应用妥善利用装备资源的同时,发挥最佳性能
4.4 页面中观察Flow的最佳方式

通过ViewModel暴露数据,并在页面中获取的最佳方式是:

  • ✔️ 利用带超时参数的 WhileSubscribed 战略暴露 Flow。示例 1
  • ✔️ 利用 repeatOnLifecycle 来网络数据更新。示例 2

最佳实践如上图所示,假如采取其他方式,上游数据流会被不绝保持活泼,导致资源浪费
当然,假如您并不必要利用到Kotlin Flow的强大功能,就用LiveData好了
5 StateFlow与SharedFlow有什么区别?

从上文着实可以看出,StateFlow与SharedFlow着实是挺像的,让人有些傻傻分不清,偶然间也挺难选择该用哪个的
我们总结一下,它们的区别如下:

  • SharedFlow设置更为机动,支持设置replay,缓冲区巨细等,StateFlow是SharedFlow的特化版本,replay固定为1,缓冲区巨细默以为0
  • StateFlow与LiveData类似,支持通过myFlow.value获取当前状态,假如有这个需求,必须利用StateFlow
  • SharedFlow支持发出和网络重复值,而StateFlow当value重复时,不会回调collect
    对于新的订阅者,StateFlow只会重播当前最新值,SharedFlow可设置重播元素个数(默以为0,即不重播)
可以看出,StateFlow为我们做了一些默认的设置,在SharedFlow上添加了一些默认束缚,这些设置可能并不符合我们的要求

  • 它忽略重复的值,而且是不可设置的。这会带来一些题目,好比当往List中添加元素并更新时,StateFlow会以为是重复的值并忽略
  • 它必要一个初始值,而且在开始订阅时会回调初始值,这有可能不是我们想要的
  • 它默认是粘性的,新用户订阅会得到当前的最新值,而且是不可设置的,而SharedFlow可以修改replay
StateFlow施加在SharedFlow上的束缚可能不是最适合您,假如不必要访问myFlow.value,而且享受SharedFlow的机动性,可以选择思量利用SharedFlow
总结

简朴通常意味着不敷强大,而强大又常常意味着复杂,两者通常不能兼得,软件开辟过程中常常面临这种弃取。
LiveData的简朴并不是它的缺点,而是它的特点。StateFlow与SharedFlow更加强大,但是学习本钱也明显的更高.
我们应该根据本身的需求公道选择组件的利用

  • 假如你的数据流比力简朴,不必要举行线程切换与复杂的数据变更,LiveData对你来说信托已经足够了
  • 假如你的数据流比力复杂,必要切换线程等操纵,不必要发送重复值,必要获取myFlow.value,StateFlow对你来说是个好的选择
  • 假如你的数据流比力复杂,同时不必要获取myFlow.value,必要设置新用户订阅重播无素的个数,大概必要发送重复的值,可以思量利用SharedFlow
参考资料

Google 保举在 MVVM 架构中利用 Kotlin Flow
Migrate from LiveData to StateFlow and SharedFlow
从 LiveData 迁移到 Kotlin 数据流
关于kotlin中的Collections、Sequence、Channel和Flow (二)
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-23 20:57, Processed in 0.178394 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表