发现在项目里还存在一些事务场景,需求完结的是单次呼应事情,运用的却是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,都是机制固定的数据流东西,作为开发者应该去了解它们的机制和原理,更好地依据事务场景去运用它们。服务于事务,保证事务的安稳牢靠才是最重要。当然,要害的是高雅 高雅!