kotlin -- Flow在实际项目中使用③

前言

主要介绍Flow在实际项目中使用。

Flow生命周期

在介绍Flow实际应用场景之前,我们先看一个Flow的计时器例子,我们在ViewModel定义了一个timeFlow数据流:

class MainViewModel : ViewModel() {

val timeFlow = flow {
    var time = 0
    while (true) {
        emit(time)
        delay(1000)
        time++
    }
}

然后Activity里面,接收前面定义的数据流

lifecycleOwner.lifecycleScope.launch {
    viewModel.timeFlow.collect { time ->
        times = time
        Log.d("ddup", "update UI $times")
    }
   }

实际效果:

flow1.gif

发现,App切换到后台时,日志还在打印,这不是对资源的浪费,我们修改一下接收的地方代码:

lifecycleOwner.lifecycleScope.launchWhenStarted {
     viewModel.timeFlow.collect { time ->
         times = time
         Log.d("ddup", "update UI $times")
     }
   }

协程开启的方法,从launch改成launchWhenStarted,再运行看下效果:

flow2.gif

我们可以看到,当点击HOME键,退回到后台的时候,日志不再打印了,由此可见,改动生效了,但是流取消接收了吗,我们切回到前台看下:

flow3.gif

切换到前台,我们可以看到,计数器并没有从0开始,所以其实它并没有取消接收,只是在后台暂停接收数据了,Flow管道还保留之前的数据,事实上这个launchWhenStarted API已经废弃了,Google更推荐repeatOnLifecycle来代替它,并且它不会存在管道中保留旧数据问题。
我们尝试改造一下对应代码:

lifecycleOwner.lifecycleScope.launch {
    lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.timeFlow.collect { time ->
            times = time
            Log.d("ddup", "update UI $times")
        }
    }
}

重新运行看下效果:

flow4.gif

我们可以看到,从后台切回到前台数据又从0开始了,说明切换到后台,Flow取消工作了,原来的数据全部清空了。

我们在使用Flow,通过repeatOnLifecycle,更能保证我们程序的安全性。

SharedFlow vs StateFlow 区别

SharedFlowStateFlow 是两个处理数据流的利器,它们基于协程,提供了一种响应式的编程方式。

原理分析

SharedFlowStateFlow 基于协程构建,它们利用协程的轻量级特性,在异步操作中更加高效。

SharedFlow 使用了一种基于事件溯源的机制,当有新的事件产生时,将事件添加到共享的事件序列中,然后通知所有订阅者。

StateFlow 则维护了一个可变的状态,并在状态发生变化时通知所有观察者。

热流与冷流

热流和冷流是关于数据流的两个基本概念,它们描述了数据流何时开始以及如何传递事件的方式。

  1. 热流是一种主动的数据流。它在创建时就开始发射事件,无论是否有观察者订阅。即使没有观察者,热流也会持续产生事件。当观察者订阅时,它只是加入了已经运行的数据流,开始接收当前已经产生的事件。
  2. 冷流是一种被动的数据流。它在有观察者订阅时才开始发射事件。每个观察者都会获得相同的事件序列,而不会受到其他观察者的影响。

SharedFlowStateFlow都是热流。即没有观察者,数据会持续更新,与LiveData类似。其中MutableSharedFlowMutableStateFlow是它们的可变类型。

SharedFlow、StateFlow与LiveData的区别

StateFlow就是SharedFlow的一种特殊类型,特点有三:

  1. 它的replay容量为 1;即可缓存最近的一次粘性事件,如果想避免粘性事件问题,使用SharedFlow,replay默认值0。
  2. 初始化时必须给它设置一个初始值
  3. 每次发送数据都会与上次缓存的数据作比较,只有不一样才会发送。它还可直接访问它自己的value参数获取当前结果值,在使用上与LiveData相似。

LiveData的不同点

  1. StateFlow必须在构建的时候传入初始值,LiveData不需要;
  2. StateFlow默认是防抖的,即相同值不更新,LiveData默认不防抖;
  3. StateFlow默认没有和生命周期绑定

StateFlow 经常被用来替代 LiveData 充当架构组件使用,所以大家相对熟悉。其实 StateFlow 只是 SharedFlow 的一种特化形式,

StateFlow 替代LiveData

前面介绍的都是Flow冷流例子,接下来将会介绍一些热流常见的应用场景。

还是前面的计时器的例子,假如横竖屏切换后,又会出现什么情况呢?

flow5.gif

我们可以看到,横竖屏切换后,,Activity重新创建,重新创建后,timeFlow会重新collect,冷流被重新collect后重新执行,然后计时器又从0开始计时了,很多时候,我们希望横竖屏切换时,希望页面的状态是保持不变的,至少在一定时间内不被改变的,这里我们冷流修改成热流试下:

======================================================================

 在协程中,通过调用操作符shareIn与stateIn,可以将一个冷流转换成一个热流,这两个方法的区别如下:

shareIn:将一个冷流转换成一个标准的热流——SharedFlow类型的对象。
stateIn:将一个冷流转换成一个单数据更新的热流——StateFlow类型的对象。

======================================================================

val hotFlow = timeFlow.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000),0)
    
    ```
lifecycleOwner.lifecycleScope.launch {
    lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.hotFlow.collect { time ->
            times = time
            Log.d("ddup", "update UI $times")
        }
    }
}
```

stateIn里面的三个参数,第一个是协程的作用域,第二个是flow保持工作状态最大有效时间,超过flow就会停止工作,最后一个参数是初始值。

重新运行看下效果:

flow6.gif

这里我们可以看到横竖屏切换后,打印的日志,计时器不会从0开始了。
我们上面介绍了一个冷流如何修改变成热流的,这里还没有介绍stateFlow如何代替LiveData,下面介绍一下,stateFlow替代LiveData用法:

private val _stateFlow = MutableStateFlow(0)
val stateFlow = _stateFlow.asStateFlow()

fun startTimer() {
    val timer = Timer()
    timer.scheduleAtFixedRate(object :TimerTask() {
        override fun run() {
            _stateFlow.value += 1
        }

    },0,1000)
}

```

viewModel.startTimer()

lifecycleOwner.lifecycleScope.launch {
    lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.stateFlow.collect { time ->
            times = time
            Log.d("ddup", "update UI $times")
        }
    }
}
```

我们定义了一个StateFlow热流,然后通过一个startTimer()方法改变stateFlow值类似LiveData setData,点击按钮时,开始改变StateFlow值并收集对应流的值类似LiveData Observe方法监听数据变化。
下面看下实际运行效果:

flow7.gif

到这里,我们介绍完了StateFlow基本用法,下面来介绍SharedFlow。

SharedFlow

SharedFlow 是继承于 Flow ,同时它是 StateFlow 的父类,它们都是是热流。

SharedFlow的特点

  • SharedFlow没有默认值
  • SharedFlow可以保存旧的数据,根据配置可以将旧的数据回播给新的订阅者
  • SharedFlow使用emit/tryEmit发射数据,StateFlow内部其实都是调用的setValue。
  • SharedFlow会挂起直到所有的订阅者处理完成。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值