Android EventBus

1,968 阅读3分钟

一.简介

EventBus是适用于 Android 和 Java 的总线事件分发机制工具。

image.png

一个事件管道上连接多个订阅者,每次发送事件,所有订阅者都能收到,每个订阅者分别对指定事件执行逻辑,其余非指定事件不作处理。

引入:

Gidhub源码: github.com/greenrobot/…

implementation("org.greenrobot:eventbus:3.3.1")

如果你需要混淆

-keepattributes *Annotation*
-keepclassmembers class * {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

# If using AsyncExecutord, keep required constructor of default event used.
# Adjust the class name if a custom failure event type is used.
-keepclassmembers class org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}

# Accessed via reflection, avoid renaming or removal
-keep class org.greenrobot.eventbus.android.AndroidComponentsImpl

二.使用

玩转EventBus主要搞清楚三个对像:「事件」「事件订阅者 」「事件发送者」

2.1 定义事件

EventBus非常强大,事件可以是任何类型对象,但实际使用过程中,我们会对事件进行简单的包装,为事件添加两个属性,事件id和事件content,如下是我的一个事件包装类

id:事件唯一id,用于标记和识别事件

content:事件携带传递的内容,可以是任何对象

class BusWrapper(val id: String, private val content: Any?) {

    /**
     * 验证通信内容是否是想要接受的
     */
    fun verifyMessage(msgId: String): Boolean = id == msgId

    /**
     * 根据messageId获取通信内容实体
     */
    fun getContent(): Any? {
        return content
    }
}

除了事件包装类,还需要一个常量类来整理归纳项目中的所有事件id:

object BusEventId {
    //普通事件1
    const val EVENT_1 = "event_1"
     //普通事件2
    const val EVENT_2 = "event_2"
    //黏性事件3
    const val STICK_EVENT_3 = "stick_event_3"
    //展示下一个界面发送的信息
    const val SHOW_MSG_FROM_NEXT_ACTIVITY = "show_msg_from_next_activity"
    ...
}

2.2 创建事件发送者

发送一般事件 直接调用

EventBus.getDefault().post(Object event)

获取到EventBus对象 再调用其post方法就可以完成一次事件发送动作

例:在SecondActivity中设置一个按钮监听 发送事件

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        findViewById<View>(R.id.btn_send_event).setOnClickListener {
            EventBus.getDefault().post(BusWrapper(BusEventId.SHOW_MSG_FROM_NEXT_ACTIVITY, "这是一条来自SecondActivity的消息"))
        }
    }
    ...
}

这里采用BusWrapper包装了一下事件 指定事件id为SHOW_MSG_FROM_NEXT_ACTIVITY,事件携带的数据内容为一段字符串 “这是一条来自SecondActivity的消息”

这样在事件发送后 所有已订阅的订阅者都会按优先级顺序收到该事件

2.3 创建事件订阅者

事件订阅需要调用订阅方法和注销订阅方法。 除此之外 还需要一个添加了@Subscribe注解的方法用户接收和处理事件 这里在MainActivity中添加事件订阅 例子:

class MainActivity  : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initView()
        EventBus.getDefault().register(this)
    }
    
      override fun onDestroy() {
        super.onDestroy()
        EventBus.getDefault().unregister(this)
    }
    
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onReceiveMessage(msg: BusWrapper) {
        Log.d("BusTest", "MainActivity onReceiveMessage:${msg.id} ")
        if (msg.verifyMessage(BusEventId.SHOW_MSG_FROM_NEXT_ACTIVITY)) {
            findViewById<TextView>(R.id.tv_event_bus_test).text = msg.getContent()?.toString()
        }
    }
    
    ...
}

可以看到这个使用@Subscribe注解了MainActivityonReceiveMessage方法,并在这个方法中接收和处理事件,因为EventBus是总线模式,onReceiveMessage会接收到所有post到EventBus中的事件,所以需要对每个接收到事件根据其事件id判断是否是需要处理的事件,如果是则执行相应逻辑,如果不需要处理,则不处理该事件。

@Subscribe 有三个参数

threadMode 线程模型ThreadMode ,它用来指定订阅者执行逻辑的线程,它有四个枚举值

  • POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。

  • MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。

  • BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。

  • ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。

priority:整数类型,默认是0,它用来指定订阅者的优先级,值越大表示优先级越大。在某个事件被发布出来的时候,优先级较高的订阅者会首先接收到事件。在优先级较高的订阅者中可以调用EventBus.getDefault().cancelEventDelivery(Object event) 来取消该事件的传递,使优先级较低的订阅者接收不到该事件。

sticky:布尔类型,它用来表示订阅者是否接收黏性事件

2.4 黏性事件

对于一个普通事件,通常情况是发送者发送事件,事件通过总线传递到接收者,接收者处理该事件,若发送事件时,订阅者尚未订阅,则接受不到该事件。

但对于黏性事件来讲,是发送者发送黏性事件,事件滞留于总线内,当订阅则订阅时,会接收到该事件。

黏性事件的发送:需要调用EventBus.getDefault().postSticky(Object event)方法

黏性事件的接收:需要在订阅者注解中添加参数sticky=true

黏性事件的移除:为了避免黏性事件一直滞留于总线内,有时候需要移除黏性事件 方法如下 移除指定黏性事件 EventBus.getDefault().removeStickyEvent(Object event) ,移除所有黏性事件EventBus.getDefault().removeAllStickyEvents()

例子:在MainActivity中发送一条黏性事件 并在SecondActivity中接收该黏性事件 MainActivity

R.id.btn_test -> {
EventBus.getDefault().postSticky(BusWrapper(BusEventId.SHOW_STICK_MSG_FROM_NEXT_ACTIVITY, "这是一条来自MainActivity的黏性事件"))
}

SecondActivity

@Subscribe(threadMode = ThreadMode.MAIN, priority = 2, sticky = true)
fun onReceiveMessage(msg: BusWrapper) {
    Log.d("BusTest", "SecondActivity onReceiveMessage:${msg.id} ")
    if (msg.verifyMessage(BusEventId.SHOW_STICK_MSG_FROM_NEXT_ACTIVITY)) {
        findViewById<TextView>(R.id.tv_event).text = msg.getContent()?.toString()
    }
}

对于一个事件封装对象,EventBus只会保留最新的黏性事件,也就意味着我们连续发送了3条通过BusWrapper封装的不同id的黏性事件,最终订阅者接收到的只是最后一条。

D/BusTest: MainActivity 发送黏性事件 id: StickEvent-1  时间戳:1640943522070
D/BusTest: MainActivity 发送黏性事件 id: StickEvent-2  时间戳:1640943523996
D/BusTest: MainActivity 发送黏性事件 id: StickEvent-3  时间戳:1640943526636
D/BusTest: SecondActivity 收到事件:StickEvent-3  事件内容:这是一条来自MainActivity的黏性事件 时间戳: 1640943526636

对于业务上确实需要多条黏性事件共存的情况,可以采用泛型的方式重新封装事件来解决

2.5 多条黏性事件共存

业务繁忙 此坑待填