注册

Kotlin-Flow常用封装类StateFlow的使用

Kotlin中StateFlow的使用


StateFlow 是 Flow 的实现,是一个特殊的流,默认的 Flow 是冷流,而StateFlow 是热流,和 LiveData 比较类似。关于冷热流后面一期 SharedFlow 会详细说明。


使用 StateFlow 替代 LiveData 应该是目前很多开发者的呼吁了,确实 LiveData 的功能 StateFlow 都能实现,可以说是 LiveData 的升级版。


StateFlow的特点



  • 它始终是有值的。
  • 它的值是唯一的。
  • 它允许被多个观察者共用 (因此是共享的数据流)。
  • 它永远只会把最新的值重现给订阅者,这与活跃观察者的数量是无关的。

官方推荐当暴露 UI 的状态给视图时,应该使用 StateFlow。这是一种安全和高效的观察者,专门用于容纳 UI 状态。


一、StateFlow的使用


方式一,我们自己 new 出来


一般我们再ViewModel中定义读写分类的StateFlow


@HiltViewModel
class Demo4ViewModel @Inject constructor(
val savedState: SavedStateHandle
) : BaseViewModel() {

private val _searchFlow = MutableStateFlow("")
val searchFlow: StateFlow<String> = _searchFlow

fun changeSearch(keyword: String) {
_searchFlow.value = keyword
}
}

在Activity中我们就可以像类似 LiveData 一样的使用 StateFlow



private fun testflow() {
mViewModel.changeSearch("key")
}

override fun startObserve() {
lifecycleScope.launchWhenCreated {
mViewModel.searchFlow.collect {
YYLogUtils.w("value $it")
}
}
}

方式二,通过一个 冷流 Flow 转换为 StateFlow


    val stateFlow = flowOf(1, 2, 3).stateIn(
scope = lifecycleScope,
// started = WhileSubscribed(5000, 1000),
// started = Eagerly,
started = Lazily,
initialValue = 1
)

lifecycleScope.launch {
stateFlow.collect {

}
}

几个重要参数的说明如下



  • scope 共享开始时所在的协程作用域范围
  • started 控制共享的开始和结束的策略
  • Lazily: 当首个订阅者出现时开始,在 scope 指定的作用域被结束时终止。
  • Eagerly: 立即开始,而在 scope 指定的作用域被结束时终止。
  • WhileSubscribed能够指定当前不有订阅者后,多少时间取消上游数据和能够指定多少时间后,缓存中的数据被丢失,回复称initialValue的值。
  • initialValue 初始值

二、替代LiveData


不管是普通的 ViewModel 观察订阅模式,在Activity中订阅,还是DataBinding的模式,我们都可以使用StateFlow来代替ViewModel


    val withdrawMethod = MutableStateFlow(0)

<ImageView
android:id="@+id/iv_giro_checked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/d_15dp"
android:src="@drawable/pay_method_checked"
android:visibility="gone"
binding:isVisibleGone="@{viewModel.withdrawMethod == 1}" />

为什么我们需要用StateFlow来代替LiveData,或者说LiveData有什么缺点?


LiveData vs Flow


先上代码,看看它们的用法与差异


ViewModel的代码


@HiltViewModel
class Demo4ViewModel @Inject constructor(
val savedState: SavedStateHandle
) : BaseViewModel() {

private val _searchLD = MutableLiveData<String>()
val searchLD: LiveData<String> = _searchLD

private val _searchFlow = MutableStateFlow("")
val searchFlow: StateFlow<String> = _searchFlow

fun changeSearch(keyword: String) {
_searchFlow.value = keyword
_searchLD.value = keyword
}
}

Activity中触发与接收事件



private fun testflow() {
mViewModel.changeSearch("key")
}

override fun startObserve() {
mViewModel.searchLD.observe(this){
YYLogUtils.w("value $it")
}

lifecycleScope.launchWhenCreated {
mViewModel.searchFlow.collect {
YYLogUtils.w("value $it")
}
}
}

可以看到基本的使用几乎是没有差异,在DataBinding中同样的是都能使用。那么它们有哪些差异呢?


它们相同的地方:



  1. 仅持有单个且最新的数据
  2. 自动取消订阅
  3. 提供「可读可写」和「仅可读」两个版本收缩权限
  4. 配合 DataBinding 实现「双向绑定」

相比StateFlow ,LiveData的确定:



  1. LiveData在某些特定的场景下会丢失数据
  2. LiveData 只能在主线程不能方便地支持异步化
  3. LiveData 的数据变换能力远远不如 Flow
  4. LiveData 粘性问题解决需要额外扩展
  5. LiveData 多数据源的合流能力远远不如 Flow
  6. LiveData 默认不支持防抖,值没有变化也会通知

这么惨,那我们开发是不是要放弃LiveData了?



恰恰不是!


如果大家全部是Koltin代码开发,那么是可以用Flow,这是基于Kotlin代码,基于协程实现的,但是现在很多项目还是 Java 语言开发的。那么LiveData还是很香的。


其二是LiveData的学习成本与 协程、Flow 的学习成本不可同日而语,开发项目是整个团队的事情,不能说你一个人会一个人用,目前LiveData的简单学习成本是很有优势的。


只是我们需要在一些特定的场景慎重使用postValue,比如数据比较秘籍的场景,我们尽量使用setValue方法。


总结


如果大家的项目的语言是 Kotlin ,并且小组成员都会 Flow 。那么我推荐你们使用StateFlow 替代LiveData 。如果不是,那么 LiveData 是你最好的选择。


谷歌也只是推荐使用Flow替代LiveData。但是并没有说打算放弃 LiveData 。并且 LiveData 与 StateFlow 都有各自的使用场景,不需要担心 LiveData的 使用。


本文我们只是简单的对比,关于StateFlow 与 SharedFlow 和LiveData 三者的差异与选择,后面等SharedFlow那一期详细的讲解。


为什么很多东西都要等SharedFlow,是因为 SharedFlow 是 StateFlow 的基础,StateFlow 像是 SharedFlow 的‘青春版’。很多东西需要讲完 SharedFlow 才能把知识点串起来,期待一下。


好了,本期内容如讲的不到位或错漏的地方,希望同学们可以指出交流。


如果感觉本文对你有一点点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。


Ok,这一期就此完结。



作者:newki
链接:https://juejin.cn/post/7127082531358244900
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册