使用ChannelFlow实现单次事件流

发现在项目里还存在一些业务场景,需要实现的是单次响应事件,使用的却是SharedFlow/StateFlow,导致部分场景下事件会丢失或多次响应。

在Flow之前有SingleEventLiveData实现单次事件流,而Flow自身却并未直接类似的方法。 作为功能更为优雅全面的Flow,不可能没有相关的实现。

于是在搜寻一番后发现了ChannelFlow这个东西,可以简单好用地实现单次事件流

使用方法

kotlin 复制代码
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个历史消息事件。

kotlin 复制代码
public fun <T> MutableSharedFlow(
    replay: Int = 0,
    extraBufferCapacity: Int = 0,
    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T> 

当设置replay=1,可以让新的订阅者接收到之前发送的事件,看起来解决了上述的问题。

然而。。设置了replay后也引来了新的问题,或者说replay机制如此产生的新效应,那就是可能导致重复处理同一个事件。

通常我们在Activity/Fragment订阅事件的方式如下:

javascript 复制代码
lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.sharedFlow.collect {
            //...
        }
    }
}

但是当页面状态发生变化,重新走到订阅流程时,就会触发replay的历史事件,又做了一次事件处理操作。

因此有一些地方为了解决此问题做了一些标志判断,比如:

javascript 复制代码
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,都是机制固定的数据流工具,作为开发者应该去了解它们的机制和原理,更好地根据业务场景去使用它们。服务于业务,保证业务的稳定可靠才是最重要。当然,关键的是优雅 优雅!

相关推荐
帅次10 小时前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
IAM四十二3 天前
Jetpack Compose State 你用对了吗?
android·android jetpack·composer
Wgllss4 天前
那些大厂架构师是怎样封装网络请求的?
android·架构·android jetpack
x02421 天前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton24 天前
深入理解观察者模式
android·kotlin·android jetpack
Wgllss24 天前
花式高阶:插件化之Dex文件的高阶用法,极少人知道的秘密
android·性能优化·android jetpack
上官阳阳1 个月前
使用Compose创造有趣的动画:使用Compose共享元素
android·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-15.属性变化控制Service
android·android studio·android jetpack
IAM四十二1 个月前
Android Jetpack Core
android·android studio·android jetpack
王能1 个月前
Kotlin真·全平台——Kotlin Compose Multiplatform Mobile(kotlin跨平台方案、KMP、KMM)
android·ios·kotlin·web·android jetpack·kmp·kmm