其他
打造一个 Kotlin Flow 版的 EventBus
作者:搬砖小子出现了
https://juejin.cn/post/6985093305470025764
背景 跨页面通信是一个比较常见的场景,通常我们会选择使用EventBus,但EventBus无法感知声明周期,收到消息就会回调,所以有了LiveData之后很快就有了LiveEventBus。不过它也有缺点,比如不能切换线程。
现在SharedFlow稳定了,那是不是也能搞一波?
背景
跨页面通信是一个比较常见的场景,通常我们会选择使用 EventBus
,但 EventBus
无法感知声明周期,收到消息就会回调,所以有了 LiveData
之后很快就有了 LiveEventBus
。不过它也有缺点,比如不能切换线程。现在 SharedFlow
稳定了,那是不是也能搞一波?
于是有了这次向大家介绍的 FlowEventBus
常见消息总线对比
消息总线 | 延迟发送 | 有序接收消息 | 粘性事件(Sticky) | 生命周期可感知 |
---|---|---|---|---|
EventBus | ❌ | ✅ | ✅ | ❌ |
RxBus | ❌ | ❌ | ✅ | ❌ |
LiveEventBus | ✅ | ✅ | ✅ | ✅ |
FlowEventBus | ✅ | ✅ | ✅ | ✅ |
设计构思
目前 AndroidX 鼓励开发者使用 StateFlow
替代 LiveData
。受此启发,以 SharedFlow
为基础,实现了 FlowEventBus
。其具备以下优点:
依托协程轻松切换线程 可以通过 replay 实现粘性效果 可以被多个观察者订阅 无观察者自动清除事件不会造成积压
结合 Lifecycle
感知生命周期,做到响应时机可控
依赖库版本
kotlinx-coroutines : 1.4.x
lifecycle-runtime-ktx : 2.3.x
API 设计
事件订阅
observeEvent<String>(eventName="SimpleEvent") { value ->
...
}
事件发送
postEvent(eventName="SimpleEvent",eventValue="Let's do it")
自定义事件订阅
observeEvent<CustomEvent> { event ->
...
}
自定义发送发送
postEvent(CustomEvent(value = "Hello Word"))
延迟发送
postDelayEvent(CustomEvent(name = "Hello Word"),1000)
发送粘性事件
postStickyEvent(eventName = STICKY,value = "☝ 粘性事件️")
指定最小感知生命周期(默认 Lifecycle.State.Started)
observeEvent<String>("SimpleEvent",Lifecycle.State.DESTROYED) { value ->
...
}
切换接收线程
observeEvent<String>("SimpleEvent",Dispatchers.IO) { value ->
...
}
实现原理
依托于 Kotlin 协程的 SharedFlow
和 Lifecycle
, 实现起来非常简单:
粘性事件
MutableSharedFlow<Any>(
replay = if (isSticky) 1 else 0, //重播给新订阅者的消息数量
extraBufferCapacity = Int.MAX_VALUE //避免挂起导致发送阻塞
)
生命周期感知
fun <T> LifecycleOwner.launchWhenStateAtLeast(
minState: Lifecycle.State,
block: suspend CoroutineScope.() -> T
) {
lifecycleScope.launch {
lifecycle.whenStateAtLeast(minState, block)
}
}
切换线程
whenStateAtLeast 由于执行的block默认是在主线程,因此需要手动切换线程:
lifecycleOwner.launchWhenStateAtLeast(minState) {
flow.collect { value ->
lifecycleOwner.lifecycleScope.launch(dispatcher) {
onReceived.invoke(value as T)
}
}
}
延迟事件
viewModelScope.launch {
delay(time)
flow.emit(value)
}
有序分发
Flow
本质类似阻塞队列, 可以保证有序。
全局单例
使用全局 ViewModel
,因为有 ViewModelScope
, 避免使用 GlobalScope
:
object ApplicationScopeViewModelProvider : ViewModelStoreOwner {
private val eventViewModelStore: ViewModelStore = ViewModelStore()
override fun getViewModelStore(): ViewModelStore {
return eventViewModelStore
}
private val mApplicationProvider: ViewModelProvider by lazy {
ViewModelProvider(
ApplicationScopeViewModelProvider,
ViewModelProvider.AndroidViewModelFactory.getInstance(EventBusInitializer.application)
)
}
fun <T : ViewModel> getApplicationScopeViewModel(modelClass: Class<T>): T {
return mApplicationProvider[modelClass]
}
}
总结
项目虽小,可以借机学习一下Flow的功能和实现原理,收获多多~
- FIN -
加好友拉你进群,技术干货聊不停
↓关注公众号↓ | ↓添加微信交流↓ |
---|---|