简体   繁体   English

使用协程和 LiveData 的 EventBus 实现

[英]EventBus implementation using Coroutines and LiveData

I need to broadcast events from different places within my app, and I need these events to be listened by different ViewModels.我需要从我的应用程序中的不同位置广播事件,并且我需要这些事件被不同的 ViewModels 监听。 What I did is that I created a "custom" implementation of EventBus using Kotlin Coroutines, Channel more specifically.我所做的是,我使用 Kotlin 协同程序创建了 EventBus 的“自定义”实现,更具体地说是Channel The implementation looks like this:实现看起来像这样:

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()
    }
}

Then in my ViewModels I use it like this然后在我的 ViewModels 中我这样使用它

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

The idea is that whatever Activity or Fragment is using this ViewModel, they can observe the message property and react to the "EventBus" events.这个想法是,无论Activity还是Fragment使用这个 ViewModel,它们都可以观察message属性并对“EventBus”事件做出反应。

What's the problem?有什么问题?

Some of these events are unreliable.其中一些事件是不可靠的。 For example, let's say ActivityA is observing messages and it prompts a Snackbar every time we get a MessageC event.例如,假设ActivityA正在观察messages ,并且每次我们收到MessageC事件时它都会提示一个Snackbar If the FlowEventBus broadcasts a MessageC event twice we'd only see the Snackbar pop once.如果FlowEventBus两次广播MessageC事件,我们只会看到Snackbar弹出一次。 I'm not very savvy about Kotlin Coroutines yet, what I think might be happening is the classing SingleLiveEvent scenario.我对 Kotlin 协程不是很了解,我认为可能发生的是分类SingleLiveEvent场景。 My guess is that the asLiveData() extension turns the Flow into a MutableLiveData and if we set the same value twice, it will just ignore it.我的猜测是asLiveData()扩展将Flow变成了MutableLiveData ,如果我们两次设置相同的值,它就会忽略它。 But I'm not sure how to introduce SingleLiveEvent here.但是我不确定这里如何引入SingleLiveEvent

Any feedback is welcome, Thanks!欢迎任何反馈,谢谢!

Despite it is not a direct answer to your question... But尽管这不是您问题的直接答案......但是

Why do you need the conversion from flow to live data?为什么需要从流量到实时数据的转换? Just subscribe to flow on ui layer and populate the flow with necessary livedata features.只需订阅 ui 层上的流并使用必要的实时数据功能填充流。 Believe me, this will make your life much more easier.相信我,这会让你的生活更轻松。

I offer to do it by using implementation androidx.lifecycle:lifecycle-runtime-ktx:xyz and SharedFlow我提议通过使用implementation androidx.lifecycle:lifecycle-runtime-ktx:xyzSharedFlow来做到这一点

In fragment :片段中

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

collectWhenStarted is a utility method which i offer to simplify subscribing a bit. collectWhenStarted是一种实用方法,我提供它来稍微简化订阅。 It utilises using of launchWhenStarted method from the library:它利用库中的launchWhenStarted方法:

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

You can create similar utilities for other lifecycle events您可以为其他生命周期事件创建类似的实用程序

To substitute a LiveData feature of persisting the data between configuration changes, background/foreground changes just convert a regular flow from domain layer to SharedFlow with replay.要替代在配置更改之间持久保存数据的LiveData功能,背景/前景更改只需将常规流从域层转换为具有重放功能的SharedFlow In a view model :视图 model 中

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

In case you are not distinct the emitted elements your EventBus shouldn't exclude any item you passed there如果您不distinct发出的元素,您的 EventBus 不应排除您传递到那里的任何项目

Check this article , the approach described there is close to what i suggest检查这篇文章,那里描述的方法接近我的建议

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM