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方法。

总结

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

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

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

为什么很多东西都要等SharedFlow,是因为 SharedFlow 是 StateFlow 的基础,StateFlow 像是 SharedFlow 的‘青春版’。很多东西需要讲完 SharedFlow 才能把知识点串起来,更多关于Kotlin Flow封装类StateFlow的资料请关注Devmax其它相关文章!

Kotlin Flow常用封装类StateFlow使用详解的更多相关文章

  1. ios – 如何旋转与照片应用程序类似的UICollectionView并保持当前视图居中?

    我有一个照片库视图使用UICollectionView与UICollectionViewFlowLayout,它有pagingEnabled和水平滚动显示一次只有一个视图.工作很好,直到我尝试旋转它当我旋转设备时,willRotatetoInterfaceOrientation:duration:我更新collectionView.contentOffset,使其保持在正确的项目上,并调整curr

  2. Kotlin难点解析:extension和this指针

    扩展是Kotlin语言中使用非常简单的一个特性。关于这个问题,其实我之前的一篇文章[[Kotlin]LambdaandExtension](https://www.jianshu.com/p/d7a...中有提到过。为了解决这个问题,官方提出了两个新的概念:dispatchreceiver和extensionreceiver。extensionreceiver:中文翻译为扩展接收者。为了简化,这里我们将dispatchreceiver简称为DR,将extensionreceiver简称为ER。如果你习惯了

  3. android – 为Flow和Mortar应用程序编写测试

    我想知道是否有任何为Flow和MortarAndroid应用编写单元测试的例子.MVP模式的部分优势在于它提供了表示和视图逻辑的分离,而表示逻辑是您要编写测试的.我希望我能看到人们如何使用Flow&迫击炮,以及他们依赖的图书馆.建立一些最佳实践会很棒.编辑:值得注意的是谷歌刚刚发布了junitsupportforAndroid.解决方法有一个u2020-mortar项目使用Mortar和F

  4. android – Kotlin类NoClassDefFoundError崩溃

    我有一个使用以下库的现有Android项目:>Autovalue>Dagger2>RxJava>Retrolambda我正在尝试添加Kotlin支持,以便我可以将项目慢慢迁移到Kotlin.这就是我所做的.>添加了Kotlin依赖.>将其中一个类转换为Kt类并转移到src/main/kotlin/..包中.>在源集中添加了kotlin.sourceSets{main.java.srcDirs=’s

  5. android – Kotlin和Dagger2

    我正在尝试将Kotlin添加到我的项目中,但在启用Kotlin之后我无法构建,因为Dagger2类不再生成.我尝试了第二个项目,我有同样的问题.这些是我为启用Kotlin所做的改变:项目build.gradle:Appbuild.gradle:错误发生在这里:其中不再定义DaggerObjectGraph.任何帮助将不胜感激.解决方法只需删除

  6. android – 在Kotlin中不能使用argb color int值吗?

    当我想在Kotlin中为TextView的textColor设置动画时:发生此错误:似乎在Kotlin中不能将值0xFF8363FF和0xFFC953BE强制转换为Int,但是,它在Java中是正常的:有任何想法吗?提前致谢.解决方法0xFF8363FF是Long,而不是Int.你必须明确地将它们转换为Int:关键是0xFFC953BE的数值是4291384254,因此它应该存储在Long变量中.但这里的高位是符号位,表示负数:-3583042,可以存储在Int中.这就是两种语言之间的区别.在Kotlin

  7. 什么是我可以使用Kotlin的最早的Android API级别?

    我认为这个问题很清楚但是我能在Kotlin上定位的最早API级别是什么?解决方法实际上,任何API级别.这是因为Kotlin被编译为JVM6平台的字节码,所有AndroidAPI级别都支持该字节码.因此,除非您在Kotlin代码中使用任何较新的AndroidAPI,否则它不需要任何特定的API级别.

  8. android – Kotlin数据类和可空类型

    我是Kotlin的新手,我不知道为什么编译器会抱怨这段代码:编译器抱怨测试?.data.length,它说我应该这样做:test?.length.但是数据变量是String,而不是String?,所以我不明白为什么我要把它?当我想检查长度.解决方法表达式test?.data部分可以为空:它是test.data或null.因此,获取其长度并不是零安全的,而是应该再次使用safecalloperator:test?.length.可空性通过整个调用链传播:你必须将这些链写成?.)).e),因为,如果其中一个左

  9. android – Kotlin自定义获取执行方法调用

    像这样的东西:仍在使用Kotlin并且不确定get()方法是否会引用编辑器而不是创建新的编辑器.解决方法第二个属性声明适合您的需要:它有一个customgetter,因此获取属性值将始终执行getter,并且不存储该值.你可能会被等号get()=…

  10. android – Kotlin合成扩展和几个包含相同的布局

    我找了一些这样的:我在Studio中看到我可以访问dayName但是dayNameTextView引用了哪一个?正常,如果我只有一个包含的布局,它工作正常.但现在我有多次包含相同的布局.我当然可以这样做:但我正在寻找好的解决方案.版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部