(StateFlow & ShareFlow) VS (Flow & LiveData)

1,702 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

在之前的Flow,collect函数浅析仿Flow构建器创建数据流文章中我们探索了flow的简单使用及它的简单原理,但是生产过程中我们往往会借用这些基础的api实现我们复杂的逻辑处理,根据需求也推出了StateFlow和SharedFlow这两个特殊的flow。接下来开始看下这几个Flow的特殊之处和应用场景吧~~

官方推荐的Flow数据流流向:

数据流程

数据流包含三个实体:

  • 提供方会生成添加到数据流中的数据。得益于协程,数据流还可以异步生成数据。
  • (可选)中介可以修改发送到数据流的值,或修正数据流本身。
  • 使用方则使用数据流中的值。

数据流中包含的实体;使用方、可选中介和提供方

大致流程

  • Producer通过MVVM的Model层提供,DDU的DataLayer层提供原始数据流;
  • Intermediary通过MVVM的ViewModel层,DDU的DomainLayer层提供转换可直接用于显示的数据流的操作
  • Consumer通过MVVM的View层,DDU的UI层使用转换后的数据流进行渲染展示数据。

flow的基础使用就不介绍了,读者可以查看开头提供的两个链接查看。

切换线程

在flow内部不允许使用不同的ConretineContext进行emit提交数据,所以想要在内部切换线程可以通过flowOn操作符进行转换

StateFlow & ShareFlow

StateFlow 和 SharedFlow 是 Flow API,允许数据流以最优方式发出状态更新并向多个使用方发出值。

这两个Flow和普通的Flow不一样, Flow我们知道,只有当调用collect的时候flow传入的函数才会执行,并且每次调用collect都会重新走一遍flow函数(本质是扩展函数),调用collect的时候执行这个扩展函数;但是这两个Flow不一样他们不依赖于外部调用(可配置稍后说明),他们是热流,他们发出的数据会缓存起来当有订阅者的时候再通知订阅者

StateFlow 和 SharedFlow是发出状态更新并向多个使用方发出值并且可以通过value属性获取当前最新值。类似于观察数据,数据更新,使用方接受最新数据是不是和LiveData很像。确实像,但他比LiveData更强大~~

StateFlow

  • 线程切换:相比于LiveData更新数据的操作只能在主线程进行,但是Flow可以通过flowOn来在不同的Dispatchers(线程分发器,CoruntineContext的一种)上运行切换线程的操作更加方便
  • 数据回溯:相比于LiveData自动管理version来决定是否通知Ovserve并且只能收到最新值的方式,Flow可通过构造函数配置reply字段决定获取之前的几次数据更新
  • 生命周期处理: 对于LiveData来说,通过观察调用observe函数的时候传入LifecycleOwner内部注册生命周期回调的方式相比;Flow的观察collect函数需要在协程中调用也就是需要自动管理协程的生命周期,否则可能会出现协程开启收到数据变化更新UI发送NPE的错误,所以需要控制好调用collect的协程域Scope的生命周期,好在Android提供了几个协程作用域的api去开启: 1.viewModelScope:跟随ViewModel的生命周期变化,当ViewModel观察的组件销毁调用onClear的时候自动取消协程,生命周期过长不采取一般在对数据进行处理的时候会使用

2.lifecycleScope: 此范围内启动的协程会在 Lifecycle 被销毁时取消;也可以通过when生命周期来指定何时开启,如果 Lifecycle 未至少处于所需的最低状态,则会挂起在这些块内运行的任何协程,注意是挂起而不是销毁时的取消,意味着还是会浪费资源。

一定要注意取消这个词和挂起的区别,挂起其实还是有订阅关系的当flow发射时还是会收到走collect上游并没有取消,但是取消就是协程作用域的取消collect函数不会执行了。

但是我们可以使用repeatOnLifecycle,它当离开某个生命周期的时候进行取消,符合的时候在开启一个新协程(也即会重新执行collect函数是新的订阅者)。

Android官方的警告:倾向于使用 repeatOnLifecycle API 收集数据流,而不是在 launchWhenX API 内部进行收集。由于后面的 API 会挂起协程,而不是在 Lifecycle 处于 STOPPED 状态时取消。上游数据流会在后台保持活跃状态,并可能会发出新的项并耗用资源

  • 需要给定一个初始值。
构建StateFlow &ShareFlow

官方示例:

将普通flow转换为ShareFlow(StateFlow的一种)通过shareIn操作符 需要传入以下三个参数: (这三个参数)

class NewsRemoteDataSource(...,
    private val externalScope: CoroutineScope,
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
}
  • externalScope:用于共享数据流的 CoroutineScope。此作用域函数的生命周期应长于任何使用方,以使共享数据流在足够长的时间内保持活跃状态。(超出该作用域时flow下游逻辑取消

repeatOnLifecycle针对生命周期取消订阅流的收集(上游)collect函数(符合在开启新协程重新订阅),WhileSubscribed策略配置订阅者超时时间进行取消flow函数(下游) 搭配好这两个一个是订阅者一个是被订阅者的关系处理好业务逻辑

  • replay:要重放 (replay) 至每个新收集器的数据项数量。(发射值的时候也会存储值,可理解为配置缓冲区大小用于之后“回放”)
  • started:“启动”行为政策。 1.WhileSubscribed()当存在活跃订阅者(观察flow的协程域没有被取消)时flow函数也会活跃(执行flow函数),可配置最后一个订阅者取消订阅的超时时间进行取消flow函数运行也可以配置数据过期时间(超过一段时间将会从缓冲中移除)

2.SharingStarted.Eagerly 可立即启动提供方(flow函数立马运行),使用 SharingStarted.Lazily 可在第一个订阅者出现后开始共享数据(只有当订阅时才会运行)并且都在externalScope作用域取消时取消收集。

行为政策链接

除此之外还可定义其他SharedFlow 行为:

  • 通过 replay,您可以针对新订阅者重新发送多个之前已发出的值。

  • 通过 onBufferOverflow,您可以指定相关政策来处理缓冲区中已存满要发送的数据项的情况。默认值为 BufferOverflow.SUSPEND,这会使调用方挂起。其他选项包括 DROP_LATEST 或 DROP_OLDEST

  • 通过 subscriptionCount 属性,获取活跃状态的收集器的数量。

  • 通过 resetReplayCache 函数清空数据缓存,供您在不想回放已向数据流发送的最新信息的情况下使用。