使用协程和 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()
// ...
想法是,无论 Activity
或 Fragment
使用此 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.z
和 SharedFlow
在片段中:
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,那里描述的方法接近我建议的方法
我需要从我的应用程序中的不同位置广播事件,并且我需要这些事件由不同的 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()
// ...
想法是,无论 Activity
或 Fragment
使用此 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.z
和 SharedFlow
在片段中:
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,那里描述的方法接近我建议的方法