发现在项目里还存在一些事务场景,需求完结的是单次呼应事情,运用的却是SharedFlow/StateFlow,导致部分场景下事情会丢掉或屡次呼应。
在Flow之前有SingleEventLiveData完结单次事情流,而Flow本身却并未直接类似的方法。
作为功能更为高雅全面的Flow,不或许没有相关的完结。
于是在搜索一番后发现了ChannelFlow这个东西,能够简略好用地完结单次事情流
运用方法
private val _eventChannel = Channel<HomeTabEvent>(capacity = Channel.CONFLATED)
val eventFlow = _eventChannel.receiveAsFlow()
// 发送方
fun sendEvent(event: HomeTabEvent) {
viewModelScope.launch {
_eventChannel.send(event)
}
}
// 接纳方
lifecycleScope.launch {
activity.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.eventFlow.collect {
...
}
}
}
运用方法与SharedFlow根本类似
在初始化变量时设置capacity = Channel.CONFLATED,保证数据池里仅有一位最新的有效值,在接纳端只会收到最新的值,并且在接纳到数据后把数据池清掉,让事情不会重复呼应
作用上与SingleEventLiveData类似
比照SharedFlow
SharedFlow是暖流,意思是数据是由发送方自己完结的,不依赖于接纳方。
因此当事情A发送时,接纳方B如果还没开端订阅,数据就现已存到数据池里。当B开端订阅后,也不会再收到历史事情A,有或许导致丢掉了事情A的处理操作。
对此状况SharedFlow供给了replay参数的设置选项,经过设置replay=n,能够让新的订阅者接纳到n个历史音讯事情。
public fun <T> MutableSharedFlow(
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T>
当设置replay=1,能够让新的订阅者接纳到之前发送的事情,看起来解决了上述的问题。
可是。。设置了replay后也引来了新的问题,或者说replay机制如此发生的新效应,那便是或许导致重复处理同一个事情。
一般我们在Activity/Fragment订阅事情的方式如下:
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.sharedFlow.collect {
//...
}
}
}
可是当页面状态发生变化,重新走到订阅流程时,就会触发replay的历史事情,又做了一次事情处理操作。
因此有一些当地为了解决此问题做了一些标志判别,比方:
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.sharedFlow.collect {
if (isHandled) {
return
}
isHandled = true
//...
}
}
}
最终也能用,便是流程和写法上过于多余,不如直接运用ChannelFlow。
总结
- 关于单次事情流,运用ChannelFlow替换StateFlow/SharedFlow更为简略且便利
- 用SharedFlow来完结单次安稳牢靠的事情流,无论设不设置replay=1,都存在运用上的问题,而ChannelFlow能够避免这些问题
- ChannelFlow有着数据线程安全,单次事情安稳的优势,能够完美替换SingleEventLiveData
虽然在单次事情场景上SharedFlow有着一些局限性,但它仍然在许多场景中有着很好的表现,它的历史报答、背压机制在许多数据流场景中都能满足。
归根到底,无论是Channel还是Flow,都是机制固定的数据流东西,作为开发者应该去了解它们的机制和原理,更好地依据事务场景去运用它们。服务于事务,保证事务的安稳牢靠才是最重要。当然,要害的是高雅 高雅!