前言
最近在自己写一些小项目,遇到了跨页面通信的需求,但传统的EventBus都有各自的缺点,如EventBus和RxBus需要自己管理生命周期,比较繁琐,基于LiveData的Bus切线程比较困难等。于是我参考了一些使用Flow实现EventBus的文章,结合自身需求,实现了极简的EventBus。
从需求出发
跨页面通讯往往是收和发,即观察者模式,其过程主要为注册、发送(携带数据)、接收(处理对应数据)、处理和解注册。而flow基于协程,可以实现自动生命周期管理,由此可以省去注册和解注册这两步。而且切线程也很方便。从需求上来说,我们期望得到如下两个API。
1. 发送Api, 传入事件。某些需求需要延时,故提供延时参数。
post(event: Event, delay: Long = 0)
2. 接收Api。需要传入LifeCycleOwner用于生命周期管理,并且使用其协程作用域,使用Dispatcher指定事件处理所在的线程,并提供处理事件的方法
observe(
lifeCycleOwner: LifeCyclerOwner,
event: Event,
dispatcher: CoroutineDispatcher = Dispathers.Main,
action: (Event) -> Unit
)
定义事件类Event
发送事件,往往需要传递数据,而传统的Event Bus可能需要定义Bundle,或者自定义数据类。在这里,我们可以把数据封装成Event类的属性,发送时设置进去就行。我们可以使用sealed class来定义事件类(具体为什么就不作过多解释,可以看一下kotlin封闭类的相关解释)
sealed class Event {
data class ShowUserName(userName: String): Event()
}
定义FlowEventBus类
主要实现前边两个API即可
object FlowEventBus {
//用HashMap存储SharedFlow
private val flowEvents = ConcurrentHashMap<String, MutableSharedFlow<Event>>()
//获取Flow,当相应Flow不存在时创建
fun getFlow(key: String): MutableSharedFlow<Event> {
return flowEvents[key] ?: MutableSharedFlow<Event>().also { flowEvents[key] = it }
}
fun post(event: Event, delay: Long = 0) {
MainScope().launch {
delay(delay)
getFlow(event.javaClass.simpleName).emit(event)
}
}
//做了一点改造,加了Lifecycle.State参数可以更精细地将控制接受到事件时的执行时机
inline fun <reified T : Event> observe(
lifecycleOwner: LifecycleOwner,
minState: Lifecycle.State = Lifecycle.State.CREATED,
dispatcher: CoroutineDispatcher = Dispathers.Main,
crossinline onReceived: (T) -> Unit
) = lifecycleOwner.lifecycleScope.launch(dispatcher = dispatcher) {
getFlow(T::class.java.simpleName).collect {
lifecycleOwner.lifecycle.whenStateAtLeast(minState) {
if (it is T) onReceived(it)
}
}
}
}
使用
//任何地方
FlowEventBus.post(Event.ShowUserName("Buer"))
//Activity或Fragment内
FlowEventBus.observe<Event.ShowUserName>(this) { it ->
textView.text = it.userName
}
总结
实现起来是很简单的,但如果要详细清楚整个的运作流程,得去了解一下SharedFlow。另外,这个只是在个人项目里使用了,真正要用到生产环境的话,可能需要额外考虑一下东西