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

标签:Kotlin,Flow,封装类,StateFlow
0
投稿

猜你喜欢

  • JAVA心得分享---return语句的用法

    2023-04-14 11:52:54
  • Android自定义带圆点的半圆形进度条

    2023-08-05 07:47:15
  • C#中ValueTuple的原理详解

    2023-10-26 06:40:34
  • C#获取真实IP地址(IP转为长整形、判断是否内网IP的方法)

    2022-01-31 08:50:06
  • C#集合之位数组的用法

    2021-07-25 18:31:08
  • Android DataBinding手把手入门教程

    2023-04-29 18:51:24
  • 微信js sdk invalid signature签名错误问题的解决方法分析

    2021-07-26 02:26:47
  • android实现图片反转效果

    2022-09-24 20:48:11
  • C#通过属性名字符串获取、设置对象属性值操作示例

    2022-01-06 14:28:08
  • @TransactionalEventListener的使用和实现原理分析

    2022-01-19 06:15:11
  • QT实现简单计算器功能

    2021-08-19 21:47:19
  • Java流程控制语句之If选择结构

    2023-11-11 04:02:29
  • ReactNative Alert详解及实例代码

    2022-07-04 15:02:46
  • Android中TextView显示插入的图片实现方法

    2023-08-06 00:27:42
  • 一键设置java 环境变量 cmd下查看、修改(覆盖与添加)等说明

    2023-03-29 07:09:25
  • 100-200之间所有素数求和程序代码(二个版本)

    2022-07-03 18:53:58
  • C#隐式运行CMD命令(隐藏命令窗口)

    2023-05-03 17:41:32
  • c# 网址压缩简单实现短网址

    2022-10-06 15:37:17
  • springboot @Async 注解如何实现方法异步

    2023-11-18 18:16:03
  • C#难点逐个击破(8):可空类型System.Nullable

    2023-07-04 14:49:40
  • asp之家 软件编程 m.aspxhome.com