Kotlin Flow响应式编程,StateFlow和SharedFlow

本文是Kotlin Flow响应式编程系列的第三篇,重点讲解了StateFlow和SharedFlow的使用。通过实例展示了如何管理Flow的生命周期,避免后台更新UI导致的问题。StateFlow作为LiveData的替代品,提供了类似的功能,而SharedFlow则解决了粘性问题,适用于非粘性场景。通过学习,读者可以更好地理解和应用这两个概念。
摘要由CSDN通过智能技术生成

6c6e20ecc69ee032ed7cd19002837ea6.jpeg

大家好,今天是Kotlin Flow响应式编程三部曲的最后一篇。

其实回想一下我写这个Kotlin Flow三部曲的初衷,主要还是因为我自己想学这方面的知识。

虽然Kotlin我已经学了很多年了,但是对于Flow我却一直没怎么接触过。可能是因为工作当中一直用不上吧,我现在工作的主语言依然还是Java。

而我一直都是这个样子,写博客基本上不是为了谁而写的,大部分都只是因为我自己想学。但是学了不用很快又会忘记,所以经常就会通过文章的形式把它记录下来,算是助人又助己了。

而Kotlin Flow在可预见的时间里,我也上不太可能能在工作当中用得到,所以这个系列也就基本是属于我个人的学习笔记了。

今天的这一篇文章,我准备讲一讲StateFlow和SharedFlow的知识。内容和前面的两篇文章有一定的承接关系,所以如果你还没有看过前面两篇文章的话,建议先去参考 Kotlin Flow响应式编程,基础知识入门Kotlin Flow响应式编程,操作符函数进阶

/   Flow的生命周期管理   /

首先,我们接着在 Kotlin Flow响应式编程,基础知识入门 这篇文章中编写的计时器例子来继续学习。

之前在编写这个例子的时候我有提到过,首要目的就是要让它能跑起来,以至于在一些细节方面的写法甚至都错误的。

那么今天我们就要来看一看,之前的计时器到底错在哪里了。

如果只是直观地从界面上看,好像一切都是可以正常工作的。但是,假如我们再添加一些日志来进行观察的话,问题就会浮出水面了。

那么我们在MainActivity中添加一些日志,如下所示:

class MainActivity : AppCompatActivity() {

    private val mainViewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView = findViewById<TextView>(R.id.text_view)
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            lifecycleScope.launch {
                mainViewModel.timeFlow.collect { time ->
                    textView.text = time.toString()
                    Log.d("FlowTest", "Update time $time in UI.")
                }
            }
        }
    }
}

这里每当计时器更新一次的时候,我们同时打印一行日志来方便进行进行观察。

另外,MainViewModel中的代码这里我也贴上吧,虽然它是完全没有改动的:

class MainViewModel : ViewModel() {

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

运行程序看一看效果:

c9dc524e4b87ecb45e598fe918b4616b.gif

一开始的时候,界面上计时器每更新一次,同时控制台也会打印一行日志,这还算是正常。

可接下来,当我们按下Home键回到桌面后,控制台的日志依然会持续打印。好家伙,这还得了?

这说明,即使我们的程序已经不在前台了,UI更新依然在持续进行当中。这是非常危险的事情,因为在非前台的情况下更新UI,某些场景下是会导致程序崩溃的。

也就是说,我们并没有很好地管理Flow的生命周期,它没有与Activity的生命周期同步,而是始终在接收着Flow上游发送过来的数据。

那这个问题要怎么解决呢?lifecycleScope除了launch函数可以用于启动一个协程之外,还有几个与Activity生命周期关联的launch函数可以使用。比如说,launchWhenStarted函数就是用于保证只有在Activity处于Started状态的情况下,协程中的代码才会执行。

那么我们用launchWhenStarted函数来改造一下上述代码:

class MainActivity : AppCompatActivity() {

    private val mainViewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView = findViewById<TextView>(R.id.text_view)
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            lifecycleScope.launchWhenStarted {
                mainViewModel.timeFlow.collect { time ->
                    textView.text = time.toString()
                    Log.d("FlowTest", "Update time $time in UI.")
                }
            }
        }
    }
}

变动就只有这一处,我们使用launchWhenStarted函数替换了之前的launch函数,其余部分都是保持不变的。

现在重新运行一下程序,效果如下图所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值