打造一个 Kotlin Flow 版的 EventBus

Posted AndroidPub

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了打造一个 Kotlin Flow 版的 EventBus相关的知识,希望对你有一定的参考价值。

背景 跨页面通信是一个比较常见的场景,通常我们会选择使用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 协程的 SharedFlowLifecycle, 实现起来非常简单:

粘性事件

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的功能和实现原理,收获多多~


Coroutines Flow:
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/index.html

仓库地址:
https://github.com/Palardin3/flow-event-bus



- FIN -


推荐阅读




加好友拉你进群,技术干货聊不停


↓关注公众号↓ ↓添加微信交流↓


以上是关于打造一个 Kotlin Flow 版的 EventBus的主要内容,如果未能解决你的问题,请参考以下文章

深潜Kotlin协程(十九):Flow 概述

深潜Kotlin协程(十九):Flow 概述

深入kotlin - Flow 进阶

深入kotlin - Flow 进阶

深入kotlin - Flow 进阶

深入kotlin - Flow 进阶