首页 > 解决方案 > SingleLiveEvent 带 Buffer 和协程

问题描述

我想构建一个SingleLiveEvent的替代品,其中有一个缓冲区,以避免在旧事件被消耗之前出现新事件时丢失事件。
目标是拥有一个发出事件的 Android ActivityViewModel,以及使用这些事件的几个片段。这意味着当一个片段被销毁时,它会停止订阅,而当一个对这些事件感兴趣的新片段开始时,它应该订阅它。换句话说,应该可以有 0、1 或更多的订阅者。

我正在尝试使用以下代码使用 kotlin 协程通道来构建它。在发出事件的 ActivityViewModel 中:

val userEvent = Channel<UserEvent>(Channel.UNLIMITED)
suspend fun onUserEvent(event: UserEvent) {
    userEvent.send(event)
}

在应该订阅的片段中:

override fun onCreateView(...) {
    (...)
    lifecycleScope.launchWhenStarted {
        sharedViewModel.userEvent.consumeEach {
            // do something with event
        }
    }

}

我面临的问题是片段消耗了这些事件,但是每当取消其范围时,通道也会被取消。然后另一个片段订阅,当 ActivityViewModel 尝试send它崩溃时,JobCancellationException因为userEvent.isClosedForSend是真的。

如果我改用 GlobalScope.launch,它不会被取消。但是后来我失去了生命周期处理(开始后开始订阅,销毁后取消)。

那么,我怎样才能保持通道打开并仍然处理生命周期呢?有没有更好的方法来做到这一点?

标签: androidandroid-livedatakotlin-coroutines

解决方案


我也在使用提到的答案,然后我意识到当用户快速浏览应用程序时它会记住以前的事件。我的解决方案是这样的:

    private val _effect = Channel<CurrenciesEffect>(1)
    val effect = _effect.receiveAsFlow().conflate()

在片段中

    private fun observeEffect() = lifecycleScope.launchWhenStarted {
        currenciesViewModel.effect.collect { viewEffect ->
            ...
        }
    }

推荐阅读