有时候我们需要实时监听一个状态变化或者持续不断接收数据,Flow提供了StateFlow和SharedFlow以供我们使用。两者的区别是StateFlow只能监听一个数据,针对的是单个数据的改变,适用于状态监听。SharedFlow是对数据流的监听,对于某一连串的事件的监听
这里记录少部分注意事项,更多的使用方式,官方已经说明了。其中StateFlow
会自动过滤重复数据,假如第二次发送的数据和第一次一样,那么第二次数据是收不到的,SharedFlow
则不会如此
SharedFlow
是Flow
的实现,StateFlow
是SharedFlow
的实现。不同于Flow
,数据使用完就关闭了,因为StateFlow和SharedFlow是用来监听状态的,所以需要手动关闭。但是又由于Flow
需要依托与协程,所以协程取消后,流会自动关闭,这里演示如下:
class ExampleUnitTest {
private val _counter = MutableStateFlow(0)
val counter = _counter.asStateFlow()
@Test
fun addition_isCorrect() {
simpleStateFlow()
}
private fun simpleStateFlow(){
private fun simpleStateFlow(){
runBlocking {
val job = launch {
counter.collect{
println("YM---->value:$it")
}
}
_counter.update{
it + 1
}
delay(100)
_counter.update{
it + 1
}
delay(100)
_counter.update{
it + 1
}
delay(100)
launch {
delay(5000)
job.cancel()
println("YM---->cancel")
}
}
}
}
}
这是个单元测试,如果不执行cancel
的话,会发现程序一直在运行,虽然已经不再发送消息。
共享流的用法大题和上述一样,区别是发送数据的方式
class ExampleUnitTest {
private val _counter = MutableSharedFlow<Int>(0)
val counter = _counter.asSharedFlow()
@Test
fun addition_isCorrect() {
simpleStateFlow()
}
private fun simpleStateFlow(){
runBlocking {
val job = launch {
counter.collect{
println("YM---->value:$it")
}
}
delay(100)
_counter.emit(3)
delay(100)
_counter.emit(4)
delay(100)
launch {
delay(5000)
job.cancel()
println("YM---->cancel")
}
}
}
}
可以看到SharedFlow
和StateFlow
最大的区别是,一个是发送不同的数据,一个是针对一个数据的改变。
在创建对象的时候会可以传一个值,默认是0
private val _counter = MutableSharedFlow<Int>()
//等于下面写法
private val _counter = MutableSharedFlow<Int>(0)
这个值的意思是缓冲数量,故名思义就是,如果没有被消耗就缓冲多少个数量的值。测试方式如下,先进行发送数据,后进行订阅数据,
private val _counter = MutableSharedFlow<Int>(0)
val counter = _counter.asSharedFlow()
private fun simpleStateFlow(){
runBlocking {
val job = launch {
delay(10)
counter.collect{
println("YM---->value:$it")
}
}
// delay(100)
_counter.emit(3)
delay(100)
_counter.emit(4)
delay(100)
launch {
delay(5000)
job.cancel()
println("YM---->cancel")
}
}
}
会发现3
这个值是无法打印的,如果修改为 private val _counter = MutableSharedFlow
时候,这个值就可以打印了,缓冲的意思就是这个
如果已经有一个流了,可以将其转换为热流。方式如下
val coldFlow: Flow<Int> = val coldFlow: Flow<Int> = (0..9).asFlow()
val shareFlow = coldFlow.shareIn(lifecycleScope, SharingStarted.Eagerly)
val stateFlow1 = coldFlow.stateIn(lifecycleScope, SharingStarted.Eagerly,
CompletableDeferred<StateFlow<Int>>()
)
private fun lifecycleListener(){
lifecycleScope.launch {
val stateFlow2 = coldFlow.stateIn(this)
}
}
SharingStarted有两个参数:
public val Eagerly: SharingStarted = StartedEagerly() //立即启用
/**
* Sharing is started when the first subscriber appears and never stops.
*/
public val Lazily: SharingStarted = StartedLazily() //延迟启用
CompletableDeferred
是一个Deferred
–一个轻量级的非阻塞 future, 这代表了一个将会在稍后提供结果的 promise。你可以使用 .await() 在一个延期的值上得到它的最终结果, 但是 Deferred 也是一个 Job,所以如果需要的话,你可以取消它。解释来自kotlin网站
CompletableDeferred
可以保证从内到外的取消i个异步协程,如果使用job.cancelAndJoin()
的话会阻塞线程池代码(该问题需未验证)。参考代码
https://stackoverflow.com/questions/52774588/coroutinescope-completabledeferred-cancellation
使用StateFlow和SharedFlow的时候需要注意生命周期,如果热流不会随着生命周期自动取消,也就是说页面消失后还会继续监听数据,或者下次进入时候会重复绑定,这样就会导致很多无法预料的错误。解决方案参考以下链接:
StateFlow
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/
StateFlow 和 SharedFlow
https://developer.android.google.cn/kotlin/flow/stateflow-and-sharedflow?hl=zh-tw
协程之StateFlow和SharedFlow介绍
https://www.kotliner.cn/2020/11/1-4-0%e5%8d%8f%e7%a8%8b%e4%b9%8bstateflow%e5%92%8csharedflow%e4%bb%8b%e7%bb%8d/
就几行代码?! 用SharedFlow写个FlowEventBus
https://blog.csdn.net/vitaviva/article/details/118854626
关于 Flow 的 shareIn 和 stateIn 运算符的注意事项
https://medium.com/androiddevelopers/things-to-know-about-flows-sharein-and-statein-operators-20e6ccb2bc74
生命周期感知型协成程
https://mp.weixin.qq.com/s/tqNLTbBB2o4O_vLlAbf8hw
协程与架构组件一起使用
https://developer.android.google.cn/topic/libraries/architecture/coroutines
组合挂起函数
CoroutineScope - CompletableDeferred 取消