Kotlin Flow常用封装类StateFlow使用详解
作者:newki 发布时间:2022-07-16 03:46:28
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中同样的是都能使用。那么它们有哪些差异呢?
它们相同的地方:
仅持有单个且最新的数据
自动取消订阅
提供「可读可写」和「仅可读」两个版本收缩权限
配合 DataBinding 实现「双向绑定」
相比StateFlow ,LiveData的确定:
LiveData在某些特定的场景下会丢失数据
LiveData 只能在主线程不能方便地支持异步化
LiveData 的数据变换能力远远不如 Flow
LiveData 粘性问题解决需要额外扩展
LiveData 多数据源的合流能力远远不如 Flow
LiveData 默认不支持防抖,值没有变化也会通知
这么惨,那我们开发是不是要放弃LiveData了?
恰恰不是!
如果大家全部是Koltin代码开发,那么是可以用Flow,这是基于Kotlin代码,基于协程实现的,但是现在很多项目还是 Java 语言开发的。那么LiveData还是很香的。
其二是LiveData的学习成本与 协程、Flow 的学习成本不可同日而语,开发项目是整个团队的事情,不能说你一个人会一个人用,目前LiveData的简单学习成本是很有优势的。
只是我们需要在一些特定的场景慎重使用postValue,比如数据比较秘籍的场景,我们尽量使用setValue方法。
来源:https://juejin.cn/post/7127082531358244900


猜你喜欢
- 在前面的一篇文章中,简单的介绍了一下如何实现软键盘不自动弹出,使用的方法是设置android:wind
- 在我们编写客户端应用程序时,经常要用到鼠标当前的位置。在C#winform中,可以用Control.MousePosition获得当前鼠标的
- Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全
- 1.注解声明:通过@interface就可以声明一个注解。@Target(ElementType.FIELD)@Retention(Rete
- 任务循环数组实现目标:(1)创建一个新的数组数据结构;(2)该数据结构为泛型;(3)可以按照元素多少进行扩容缩容;(4)进行添加删除操作的时
- 方法1:import java.net.HttpURLConnection;import java.net.URL;import org.j
- 异步接口的声明我们已经了解到,如果一个方法是异步的,那么这个方法的返回值类型是Task<T>,那么接口中该如何规定异步方法呢?一
- 在Android中实现异步任务机制有两种方式,Handler和AsyncTask。Handler模式需要为每一个任务创建一个新的线程,任务完
- 最近接了个项目其中有需要要实现此功能:seekbar需要显示最左和最右值,进度要跟随进度块移动。下面通过此图给大家展示下效果,可能比文字描述
- 如下所示:for (int i= 0; i<= 1084; i++) {if (String.valueOf(i+1).length(
- 我们用NuGet还原.NET Core项目会报以下错误:error NETSDK1064: 未找到版本为 1.8.2 的包 BouncyCa
- 一、案例场景在使用 @Autowired 时,你或多或少都会遇过类似的错误:required a single bean, but 2 we
- 问题背景昨晚同事找我帮他看一个问题,他使用mybatis-plus中提供的updateById方法,想将查询结果中某个字段原本不为null的
- 1.引入AOP依赖<dependency>
- 一、缩略图在浏览相册的时候,可能需要生成相应的缩略图。直接上代码:public class ImageUtil { private Logg
- 简介最近花了两天时间研究使用Flutter开发一个抖音国际版. 个人感觉使用Flutter开发app快得不要不要的额. 两天就基本可以开发个
- 开发设计搞了一个带圆形进度的进度条,在GitHub上逛了一圈,发现没有,自己撸吧。先看界面效果:主要思路是写一个继承ProgressBar的
- <script>//验证身份证号方法var test=function(idcard){var Errors=new Array
- 1. 简单介绍嗨,大家好,今天给想给大家分享一下关于Mybatis-plus 的 Service 层的一些方法的使用。今天没有总结,因为都是
- 一、首先在主布局中,用帧布局来填充 RecycleView 和 两个模拟发送消息的Button<?xml version="