前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >手把手教你如何用 100 行代码实现一个有生命周期感知能力的 EventBus

手把手教你如何用 100 行代码实现一个有生命周期感知能力的 EventBus

作者头像
GeeJoe
发布于 2021-12-08 12:50:33
发布于 2021-12-08 12:50:33
56500
代码可运行
举报
文章被收录于专栏:掘金文章掘金文章
运行总次数:0
代码可运行

事件总线是一个项目开发中必不可少的能力,市面上也有几个非常有名的事件库,比如 EventBus 以及基于 RxJava 的 RxBus 等

但是他们在使用的时候都必须要手动注册/反注册监听,我们能否实现一个不需要手动反注册的事件总线呢,换句话说,我们如何实现一个能够在生命周期 destroy 的时候自定解绑监听的事件总线呢

1. 一个 EventBus 的简易模型

首先简单构思一下我们的 EventBus —— 任意对象都可以作为事件被发送,通过对象的 Class 类型来对事件进行订阅和分发

我们先定义一下 LifecycleEventBus 的几个基础方法(暂不考虑生命周期感知能力)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
object LifecycleEventBus {
    // 添加监听
    fun <T : Any> observe(eventType: Class<T>, observer: EventObserver<T>) {}
  
     // 移除监听
    fun <T : Any> removeObserver(observer: EventObserver<T>) {}
  
     // 发送事件
     fun <T: Any> sendEvent(event: T) {}
}
复制代码

抽象的监听者接口 —— EventObserver

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface EventObserver<T : Any> {
    // 收到事件的时候回调函数,业务方在这里实现对事件的处理逻辑
    fun onEvent(event: T)
}
复制代码

这样一个简易的 EventBus 就搭建好了,核心思路是:以事件的 Class 类型作为 key,对应的 Observer 作为 value,将此 key-value 存储起来,在发送事件的时候,根据传入的 event 的 Class 类型,找到对应的 Observer 然后调用其 onEvent() 方法来分发事件,实现代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private val observerMap =
        mutableMapOf<Class<*>, MutableList<EventObserver<*>>>()

private fun addObserver(eventType: Class<*>, observer: EventObserver<*>) {
    val observers = observerMap[eventType] ?: MutableList()
    if (observerMap[eventType] == null) {
        observerMap[eventType] = observers
    }
  	if (!observers.contains(observer)) {
      	observers.add(observer)
    }
}

fun <T: Any> sendEvent(event: T) {
  	val eventType = event::class.java
        observerMap[eventType]?.forEach { observer ->
    		observer.onEvent(event)
        }
}

复制代码

2. 让 Observer 具备生命周期感知能力

借助 androidx-lifecycle 中的 LifecycleEventObserver 我们可以让 EventObserver 具备生命周期感知能力

如果对 LifecycleEventObserver 还比较陌生的同学,可以看看我的另一篇文章《自定义生命周期以及实现生命周期感知能力 》

为了兼容无生命周期的组件,我们同时也要保留非生命周期感知能力的 Observer,为此,我们可以抽象一个 Observer 包装器 —— ObserverWrapper

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
open class ObserverWrapper(val observer: EventObserver<*>) {
    // 用于解绑生命周期,后续介绍	
  open fun detachObserver() {}
}
复制代码

实现一个生命周期感知能力的 Observer —— LifecycleBoundObserver

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private class LifecycleBoundObserver(
        private val owner: LifecycleOwner,
        observer: EventObserver<*>
    ) : ObserverWrapper(observer), LifecycleEventObserver {

        init {
            // 监听生命周期的变化
            owner.lifecycle.addObserver(this)
        }

        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            val currentState: Lifecycle.State = source.lifecycle.currentState
            // 当生命周期即将销毁的时候,移除监听器
            if (currentState == Lifecycle.State.DESTROYED) {
                removeObserver(observer)
                return
            }
        }

  	// 在移除监听 removeObserver 中回调被移除的 observer 的
  	// detachObserver 方法来解绑对生命周期的监听
        override fun detachObserver() {
            super.detachObserver()
            owner.lifecycle.removeObserver(this)
        }
    }
复制代码

然后添加监听的地方修改如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 由于我们使用了包装类 ObserverWrapper,但是对于外部而言该包装类是不可见的,为了能够
// 正常移除 observer,我们需要建立原始 observer 和 ObserverWrapper 的映射关系
// 这里表现为一个 HashMap
private val observerMap =
        mutableMapOf<Class<*>, ConcurrentHashMap<EventObserver<*>, ObserverWrapper>>()

private fun addObserver(eventType: Class<*>, observerWrapper: ObserverWrapper) {
        val observers = observerMap[eventType] ?: ConcurrentHashMap()
        if (observerMap[eventType] == null) {
            observerMap[eventType] = observers
        }
        observers.putIfAbsent(observerWrapper.observer, observerWrapper)
    }
复制代码

同时在移除监听器的时候也要把对生命周期的监听移除:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fun <T : Any> removeObserver(observer: EventObserver<T>) {
        observerMap.forEach { (_, observers) ->
        val wrapper = observers.remove(observer)
        // 移除监听的时候,同时也需要移除 lifecycle 的监听
        wrapper?.detachObserver()
        }
    }
复制代码

如此,我们就实现了一个具备生命周期感知能力的 Observer,在使用的时候传入对应的 LifecycleOwner 就可实现自动解绑监听

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
LifecycleEventBus.observe(lifecycleOwner, MyEvent::class.java, observer)
复制代码

而对于不需要生命周期感知能力的 Observer,我们直接使用 ObserverWrapper 就可以了,代码很简单,这里不再赘述

3. 支持线程切换

当前的实现 observer 将运行在 sendEvent() 所在的线程,很多时候,我们可能在子线程发送事件,但是期望在主线程监听,那么我们就需要实现线程切换能力,让 Observer 可以运行在指定的线程上

定义 Enum 线程模式 —— ThreadMode

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/** 
* ORIGIN: Observer 将运行在发送事件所在的线程
* MAIN: Observer 将运行在主线程
*/
enum class ThreadMode {ORIGIN, MAIN}
复制代码

observe() 方法增加参数,默认是 ThreadMode.ORIGIN

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fun <T : EVENT> observe(
        owner: LifecycleOwner,
        eventType: Class<T>,
        observer: EventObserver<T>,
        threadMode: ThreadMode = ThreadMode.ORIGIN
    ) {
        addObserver(eventType, LifecycleBoundObserver(owner, observer, threadMode))  
    }
复制代码

threadMode 传递到 observer 中,当分发事件的时候,判断如果 threadModeThreadMode.MAIN 切换到主线程即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  if (threadMode == ThreadMode.MAIN) {
            ThreadManager.runOnMainThread {
                onEvent(it)
            }
        } else {
            onEvent(it)
        }
复制代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
object ThreadManager {

    private val mainHandler by lazy {
        Handler(Looper.getMainLooper())
    }

    fun runOnMainThread(block: () -> Unit) {
        if (isMainThread()) {
            block()
        } else {
            mainHandler.post(block)
        }
    }

    private fun isMainThread(): Boolean {
        return Looper.myLooper() == Looper.getMainLooper()
    }
}
复制代码

至此,一个完整的具备生命周期感知能力的 EventBus 就完成了,整体代码 100 左右,十分精简。源码详情请到 GitHub 自行查看:LifecycleEventBus

Overview

  • 支持绑定 Lifecycle,能够在生命周期 onDestroy 的时候自动移除监听,可与 Android Jetpack 中的 Lifecycle 组件无缝衔接
  • 支持监听者线程切换
  • 支持手动注册/反注册监听器
  • 代码精简,只有 100 行左右

相比 EventBus/RxBus 优势:

  • EventBus 中事件分发采用反射,LifecycleEventBus 以接口形式回调,不存在反射
  • RxBus 依赖 RxJava,对包大小有影响,LifecycleEventBus 代码精简,只有 100 行左右
  • LifecycleEventBus 具备 EventBus 和 RxBus 没有的「生命周期感知能力」

Sample

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// difine an event
data class LoginEvent(val userId: String)
// define an observer 
val observer = object : EventObserver<LoginEvent> {
            override fun onEvent(event: LoginEvent) {
                println("onEvent --> $event")
            }
        }
// add observer with lifecycleOwner.
// the observer will be removed when lifecycle goes to destroy
LifecycleEventBus.observe(this, LoginEvent::class.java, observer)
// send event
LifecycleEventBus.sendEvent(LoginEvent("12345"))
复制代码
相关文章:

自定义生命周期以及实现生命周期感知能力

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年05月26日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
自定义生命周期以及实现生命周期感知能力
在 Android 的世界里,很多组件都是具备生命周期的,比如 Activity、Fragment 等,在 Architecture Component 出来之前,我们都是通过 Activity/Fragment 的生命周期回调函数来在相应的生命周期里做相应的操作,比如注册监听、释放资源等等。
GeeJoe
2021/12/09
1K0
自定义生命周期以及实现生命周期感知能力
Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus
对于Android系统来说,消息传递是最基本的组件,每一个App内的不同页面,不同组件都在进行消息传递。消息传递既可以用于Android四大组件之间的通信,也可用于异步线程和主线程之间的通信。对于Android开发者来说,经常使用的消息传递方式有很多种,从最早使用的Handler、BroadcastReceiver、接口回调,到近几年流行的通信总线类框架EventBus、RxBus。Android消息传递框架,总在不断的演进之中。
美团技术团队
2019/03/29
2.4K0
Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus
LiveData的前世今生
这个系列我做了协程和Flow开发者的一系列文章的翻译,旨在了解当前协程、Flow、LiveData这样设计的原因,从设计者的角度,发现他们的问题,以及如何解决这些问题,pls enjoy it。
用户9239674
2021/12/01
1.1K0
4. Jetpack源码解析—LiveData的使用及工作原理
2. Jetpack源码解析—Navigation为什么切换Fragment会重绘?
Hankkin
2019/07/10
1.1K0
LiveData详细分析
目录介绍 01.LiveData是什么东西 02.使用LiveData的优势 03.使用LiveData的步骤 04.简单使用LiveData 05.observe()和observerForever() 06.LiveData原理介绍 07.observe订阅源码分析 08.setValue发送源码分析 09.observeForever源码 10.LiveData源码总结 00.使用LiveData实现bus事件总线 利用LiveData实现事件总线,替代EventBus。充分利用了生命周期感知功能,可
杨充
2020/03/05
3K0
Jetpack组件之LiveData
ViewModel 以注重生命周期的方式存储和管理界面相关的数据,当数据发生变化时,可通过接口的方式通知页面,但是有很多数据要通知时,需要定义大量的接口显得十分冗余,为此,Jetpack提供了LiveData组件解决这种问题,简化了开发过程。 LiveData 是一种可观察的数据存储器类。它是一个数据的容器,将数据包装起来,使数据成为被观察者,当数据发生变化时,观察者能够获得通知。 LiveData 具有生命周期感知能力,它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
八归少年
2022/06/29
8650
【AAC 系列三】深入理解架构组件:LiveData
在之前我们深入研究了 Lifecycle 的实现原理,并在文末提到了LiveData 以及 ViewModel,这次我们来讲讲 LiveData。
程序亦非猿
2019/08/16
9320
【AAC 系列三】深入理解架构组件:LiveData
Android | LiveData 源码分析
LiveData 是一种持有可被观察的数据存储类,和其他可被观察的类不同的是,LiveData 是就要生命周期感知能力的,这意味着他可以在 Activity ,fragment 或者 service 生命周期活跃状态时 更新这些组件。
345
2022/09/27
1.1K0
Android livedata 源码解剖
本次推出 Android Architecture Components 系列文章,目前写好了四篇,主要是关于 lifecycle,livedata 的使用和源码分析,其余的 Navigation, Paging library,Room,WorkMannager 等春节结束之后会更新,欢迎关注我的公众号,有更新的话会第一时间会在公众号上面通知。
程序员徐公
2019/02/26
9980
LiveData 非粘性消息的探索和尝试
LiveData 默认是支持粘性消息的(关于什么是粘性消息,请移步我的另一篇文章:LiveData 的正确使用姿势以及反模式 ),如何通过 LiveData 来实现非粘性消息呢,本文将在官博的基础上,分析几种尝试的方案,以及他们各自的优缺点
GeeJoe
2021/12/09
9980
Android Architecture Component之LiveData解析HeaderLiveDataFooter
Android Architecture Component 是 Google 在 2017 年推出的一套帮助开发者解决 Android 架构设计的方案。里面有众多吸引人的亮点,比如 Lifecycle、ViewModel 和 LiveData 等组件的设计,确实是一款牛逼的架构。
俞其荣
2018/07/25
6000
Lifecycle:生命周期感知型组件的基础 —— Jetpack 系列(1)
Lifecycle 的主要作用是简化实现生命周期感知型组件的复杂度。 在传统的方式中,需要手动从外部宿主(如 Activity、Fragment 或自定义宿主)中将生命周期事件分发到功能组件内部,这势必会造成宿主代码复杂度增加。例如:
用户9995743
2022/09/26
1.2K0
Lifecycle:生命周期感知型组件的基础 —— Jetpack 系列(1)
从 Android 开发到读懂源码 第04期:LiveData 源码解析
LiveData is a data holder class that can be observed within a given lifecycle. 正如注释所说,LiveData 是一个数据持有容器,并且该容器可以感知生命周期的变化,在合适的时机通知观察者数据的变更。
数据库交流
2022/04/25
5170
从 Android 开发到读懂源码 第04期:LiveData 源码解析
事件总线方案实践
private final Runnable mPostValueRunnable = new Runnable() {
杨充
2020/03/13
1.9K0
“终于懂了“系列:Jetpack AAC完整解析(二)LiveData 完全掌握!
也就是说,LiveData使得 数据的更新 能以观察者模式 被observer感知,且此感知只发生在 LifecycleOwner的活跃生命周期状态。
胡飞洋
2020/12/15
4.1K0
浅谈Lifecycle的原理,以及liveData在Lifecycle的扮演者
我们知道jetpack出现给android开发带来很大的便利,尤其是Lifecycle带来的方便,作为一个有志向的码农,就应该了解一下Lifecycle的原理
包子388321
2020/06/16
1.4K0
Jetpack:LiveData
LiveData是一个可被观察的数据持有者类。与常规的Observable不同,LiveData能意识到应用程序组件的生命周期变化,这意味着它能遵守Activity、Fragment、Service等组件的生命周期
提莫队长
2020/06/03
6210
Android AAC架构实践
本文就如上问题结合aac框架源码进行逐步解析 ##一.LiveData实现数据更新 既然是监测数据更新,肯定是使用到观察者模式
用户9227784
2021/12/15
1K0
Android Architecture Components Part3:Lifecycle
上期文章我们讲解了LiveData,知道它是一个可观察容器同时具备生命感知能力。那么它的生命感知能力又是如何实现呢?在使用LiveData进行注册observer时,是否记得其中的两个参数。其一是Observer的回调,另一个就是LifecycleOwner。它就属于Lifecycle中的一部分。
Rouse
2019/07/16
6100
Android Architecture Components Part3:Lifecycle
美团的技术实力怎么样?
大家好,我是小彭。2 年前,我们在 为了组件化改造学习十几家大厂的技术博客[2] 这篇文章里收集过各大厂的组件化方案。其中,有美团收银团队分享的组件化总线框架 modular-event 让我们印象深刻。然而,美团并未将该框架开源,我们只能望梅止渴。
用户9995743
2022/12/22
7770
美团的技术实力怎么样?
相关推荐
自定义生命周期以及实现生命周期感知能力
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验