转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/121911675
本文出自【赵彦军的博客】
文章目录
系列文章
Kotlin实战指南二十:flow
Android SharedFlow详解
Android StateFlow详解
什么是SharedFlow
和 StateFlow
一样,SharedFlow
也是热流,它可以将已发送过的数据发送给新的订阅者,并且具有高的配置性。SharedFlow
是一个接口,继承 Flow
在使用是,一般使用 MutableSharedFlow
SharedFlow 有如下特点:
1、是热数据流 ,及时没有接收者,也会发射数据
2、SharedFlow 是 StateFlow 的可配置性极高的泛化数据流。
3、可以有多个接收器,一个数据可以被多个接收
实战
class MainActivity : AppCompatActivity() {
private val mutableSharedFlow = MutableSharedFlow<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launch {
mutableSharedFlow.collect {
Log.d("bus-", "观察者1,$it")
}
mutableSharedFlow.collect {
Log.d("bus-", "观察者2,$it")
}
}
//发射数据
lifecycleScope.launch {
mutableSharedFlow.emit(System.currentTimeMillis().toString())
}
}
}
日志:
D/bus-: 观察者1,1639392387467
很奇怪,观察者2 没有输出日志。我们的期望是 发射一个数据,可以被两个观察者接收,但实际只有一个观察者接收了数据。
事实上 第二个 观察者的代码就没有执行。也就是 下面这段代码 没有被执行。
mutableSharedFlow.collect {
Log.d("bus-", "观察者2,$it")
}
collect 是挂起函数,因为他可以不断的接收值,会不断的挂起,不断的恢复。具体实现在 SharedFlowImpl 中。
可以这么写,用两个协程去接收值:
class MainActivity : AppCompatActivity() {
private val mutableSharedFlow = MutableSharedFlow<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launch {
mutableSharedFlow.collect {
Log.d("bus-", "观察者1,$it")
}
}
lifecycleScope.launch {
mutableSharedFlow.collect {
Log.d("bus-", "观察者2,$it")
}
}
//发射数据
lifecycleScope.launch {
mutableSharedFlow.emit(System.currentTimeMillis().toString())
}
}
}
日志输出:
D/bus-: 观察者1,1639393316855
D/bus-: 观察者2,1639393316855
默认无粘性
SharedFlow 默认无粘性的,也就是后面的观察者不能收到前面已经发射的数据。
当然也有api,支持。
replay 代表重放的数据个数
replay 为0 代表不重放,也就是没有粘性
replay 为1 代表重放最新的一个数据,后来的接收器能接受1个最新数据。
replay 为2 代表重放最新的两个数据,后来的接收器能接受2个最新数据。
示例如下:
class MainActivity : AppCompatActivity() {
private val mutableSharedFlow = MutableSharedFlow<String>(replay = 2)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launch {
mutableSharedFlow.collect {
Log.d("bus-", "观察者1,$it")
}
}
lifecycleScope.launch {
mutableSharedFlow.collect {
Log.d("bus-", "观察者2,$it")
}
}
//发射数据
lifecycleScope.launch {
mutableSharedFlow.emit("a")
mutableSharedFlow.emit("b")
mutableSharedFlow.emit("c")
}
lifecycleScope.launch {
delay(2000)
mutableSharedFlow.collect {
Log.d("bus-", "观察者3,$it")
}
}
}
}
输出日志
D/bus-: 观察者1,a
D/bus-: 观察者1,b
D/bus-: 观察者2,a
D/bus-: 观察者2,b
D/bus-: 观察者2,c
D/bus-: 观察者1,c
D/bus-: 观察者3,b
D/bus-: 观察者3,c
观察者1 ,观察者2 都接受到了三个数据,分别是 a、b、c
观察者3 是后来加入的,可以接收到最新的两个数据 b、c
extraBufferCapacity 额外的数据缓存池
Flow 存在发送过快,消费太慢的情况,这种情况下,就需要使用缓存池,把未消费的数据存下来。
缓冲池容量 = replay + extraBufferCapacity
如果总量为 0 ,就 Int.MAX_VALUE
onBufferOverflow
如果指定了有限的缓存容量,那么超过容量以后怎么办?
- BufferOverflow.SUSPEND : 超过就挂起,默认实现
- BufferOverflow.DROP_OLDEST : 丢弃最老的数据
- BufferOverflow.DROP_LATEST : 丢弃最新的数据
什么情况下用 SharedFlow
当你有如下场景时,需要使用 SharedFlow:
- 发生订阅时,需要将过去已经更新的 n 个值,同步给新的订阅者。
- 配置缓存策略。
- 有多个订阅者。
tryEmit
当 MutableSharedFlow
中缓存数据量超过阈值时,emit
方法和 tryEmit
方法的处理方式会有不同:
emit
方法:当缓存策略为BufferOverflow.SUSPEND
时,emit
方法会挂起,直到有新的缓存空间。tryEmit
方法:tryEmit
会返回一个Boolean
值,true
代表传递成功,false
代表会产生一个回调,让这次数据发射挂起,直到有新的缓存空间。
将冷流转化为SharedFlow
直接使用官网的代码,方法是使用 Flow
的扩展方法 shareIn
:
class NewsRemoteDataSource(...,
private val externalScope: CoroutineScope,
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
...
}.shareIn(
externalScope,
replay = 1,
started = SharingStarted.WhileSubscribed() // 启动政策
)
}
重点是参数三,分别提供了三个启动策略:
SharingStarted.WhileSubscribed()
:存在订阅者时,将使上游提供方保持活跃状态。SharingStarted.Eagerly
:立即启动提供方。SharingStarted.Lazily
:在第一个订阅者出现后开始共享数据,并使数据流永远保持活跃状态。
总结
Flow
给我的感觉就像古老的印刷术,版面定了就不可更改,不过,该版面可印刷多张内容;StateFlow
给我的感觉就像活字印刷,可以不停的更改版面,也可以使用同一个版面印刷很多内容。
如果你要使用 Flow
记录数据的状态,StateFlow
和 SharedFlow
会是一个不错的选择。StateFlow
和 SharedFlow
提供了在 Flow
中使用 LiveData
式更新数据的能力,但是如果要在 UI
层使用,需要注意生命周期的问题。
StateFlow
和 SharedFlow
相比,StateFlow
需要提供初始值,SharedFlow
配置灵活,可提供旧数据同步和缓存配置的功能。