【Kotlin】就几行代码?! 用SharedFlow写个FlowEventBus

在这里插入图片描述

作者:搬砖小子出现了
链接:https://juejin.cn/post/6985093305470025764

背景

跨页面通信是一个比较常见的场景,通常我们会选择使用 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]
    }
}

总结

站在巨人的肩膀上的同时也可以简单了解下原理, 不过挺复杂的,需要下点功夫😄

  • coroutine-flow: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/index.html
  • 仓库地址:https://github.com/Palardin3/flow-event-bus
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值