使用协程和 LiveData 的 EventBus 实现

EventBus implementation using Coroutines and LiveData

我需要从我的应用程序中的不同位置广播事件,并且我需要这些事件由不同的 ViewModel 侦听。我所做的是,我使用 Kotlin 协程创建了 EventBus 的“自定义”实现,Channel 更具体地说。实现如下所示:

interface FlowEventBus {

    sealed class MessageEvent {
        data class MessageA(
            val someData: Int
        ) : MessageEvent()
        data class MessageB(
            val someOtherData: String
        ) : MessageEvent()
        object MessageC : MessageEvent()
    }

    suspend fun postMessage(messageEvent: MessageEvent)
    fun flowMessage(): Flow<MessageEvent>
}

class FlowEventBusImpl() : FlowEventBus {
    private val channel = Channel<FlowEventBus.MessageEvent>()

    override suspend fun postMessage(messageEvent: FlowEventBus.MessageEvent) {
        channel.send(messageEvent)
    }

    override fun flowMessage(): Flow<FlowEventBus.MessageEvent> {
        return channel.receiveAsFlow()
    }
}

然后在我的 ViewModels 中我这样使用它

@HiltViewModel
class SomeViewModel @Inject constructor(
    private val flowEventBus: FlowEventBus
) : ViewModel() {
    
    val message = flowEventBus.flowMessage().asLiveData()
    // ...

想法是,无论 ActivityFragment 使用此 ViewModel,他们都可以观察 message 属性 并对“EventBus”事件做出反应。

有什么问题吗?

其中一些事件不可靠。例如,假设 ActivityA 正在观察 messages 并且每次我们收到 MessageC 事件时它都会提示 Snackbar。如果 FlowEventBus 广播 MessageC 事件 两次 我们只会看到 Snackbar 弹出一次。 我对 Kotlin Coroutines 不是很了解,我认为可能发生的是分类 SingleLiveEvent 场景。我的猜测是 asLiveData() 扩展将 Flow 变成了 MutableLiveData,如果我们两次设置相同的值,它就会忽略它。但是这里不知道怎么介绍SingleLiveEvent

欢迎任何反馈, 谢谢!

尽管它不是直接回答你的问题...但是

为什么需要从流量到实时数据的转换?只需订阅 ui 层上的流,并使用必要的实时数据功能填充流。相信我,这会让你的生活更轻松。

我提议使用 implementation androidx.lifecycle:lifecycle-runtime-ktx:x.y.zSharedFlow

片段中:

with(viewModel) {
    eventsFlow.collectWhenStarted(viewLifecycleOwner, ::processEvents)
}

collectWhenStarted 是一种实用方法,我提供它来稍微简化订阅。它利用库中的 launchWhenStarted 方法:

inline fun <T> Flow<T>.collectWhenStarted(
    lifecycleOwner: LifecycleOwner,
    crossinline action: suspend (T) -> Unit
): Job = lifecycleOwner.lifecycleScope.launchWhenStarted {
    collectLatest {
        action.invoke(it)
    }
}

您可以为其他生命周期事件创建类似的实用程序

要替代 LiveData 在配置更改之间持久保存数据的功能,background/foreground 更改只是将常规流从域层转换为 SharedFlow 并重放。 在 视图模型中:

val eventsFlow: Flow<DataClass> = flowEventBus
    .flowMessage()
    .shareIn(viewModelScope, SharingStarted.Lazily, 1)

如果您不是 distinct 发出的元素,您的 EventBus 不应排除您传递到那里的任何项目

检查这个 article,那里描述的方法接近我建议的方法