
點(diǎn)贊關(guān)注,不再迷路,你的支持對(duì)我意義重大!
?? Hi,我是丑丑。本文 「Android 路線」| 導(dǎo)讀 —— 從零到無窮大 已收錄,這里有 Android 進(jìn)階成長(zhǎng)路線筆記 & 博客,歡迎跟著彭丑丑一起成長(zhǎng)。(聯(lián)系方式在 GitHub)
前言
- 在 Android 開發(fā)中,EventBus 事件總線機(jī)制十分常用;
- 今天,我將整理 EventBus 詳細(xì)的使用教程,追求簡(jiǎn)單易懂又不失深度。如果能幫上忙,請(qǐng)務(wù)必點(diǎn)贊加關(guān)注,這真的對(duì)我非常重要。
目錄

前置知識(shí)
這篇文章的內(nèi)容會(huì)涉及以下前置 / 相關(guān)知識(shí),貼心的我都幫你準(zhǔn)備好了,請(qǐng)享用~
- APT: 【點(diǎn)贊催更】
- 注解: 《Java | 注解(含 Kotlin)》
- 混淆: 《Android | 代碼混淆到底做了什么?》
1. EventBus 概述
- 定義:一套
Android / Java事件訂閱 / 發(fā)布框架,由 greenrobot 團(tuán)隊(duì)開源。

- 作用:在組件 / 線程間通信的場(chǎng)景中,將數(shù)據(jù)或事件傳遞給對(duì)應(yīng)的訂閱者。

- 為什么要使用
EventBus(特點(diǎn))?
在 Android 組件 / 線程間通信的應(yīng)用場(chǎng)景中,EventBus 比傳統(tǒng)的接口監(jiān)聽、Handler、Executors、LocalBroadcastManager 更簡(jiǎn)潔可靠,具體描述如下:- 1、使用事件總線框架,實(shí)現(xiàn)事件發(fā)布者與訂閱者松耦合;
- 2、提供了透明線程間通信,隱藏了發(fā)布線程與訂閱線程間的線程切換。

- EventBus 相關(guān)概念
關(guān)于EventBus機(jī)制的相關(guān)概念如下:

2. 使用步驟
在分析 EventBus 的使用原理之前,我們先來介紹下 EventBus 的使用步驟。
2.1 步驟1:添加依賴
- 在 module 級(jí)
build.gradle中添加依賴:
dependencies {
def eventbus_version = '3.2.0'
implementation "org.greenrobot:eventbus:$eventbus_version"
}
- 使用編譯時(shí)索引時(shí),還需要依賴 注解處理工具,注意:純 Java 項(xiàng)目和 Kotlin 使用的注解處理工具不同:
- Java 項(xiàng)目使用
annotationProcessor - Kotlin 項(xiàng)目使用
kapt
- Java 項(xiàng)目使用
// Java:
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.have.a.good.MyEventBusAppIndex' ]
}
}
}
}
dependencies {
def eventbus_version = '3.2.0'
implementation "org.greenrobot:eventbus:$eventbus_version"
annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbus_version"
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied
dependencies {
def eventbus_version = '3.2.0'
implementation "org.greenrobot:eventbus:$eventbus_version"
kapt "org.greenrobot:eventbus-annotation-processor:$eventbus_version"
}
kapt {
arguments {
arg('eventBusIndex', 'com.have.a.good.MyEventBusAppIndex')
}
}
2.2 步驟2:準(zhǔn)備訂閱者
訂閱者需要實(shí)現(xiàn)訂閱方法,并使用@Subscribe注解修飾,具體要求如下:

舉例:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
@Subscribe 注解參數(shù)中,threadMode參數(shù)決定了使用的線程模型,目前一共有五種:

2.3 步驟3:注冊(cè)與注銷
在發(fā)布事件之前,需要先 注冊(cè)訂閱者。而在訂閱者生命周期結(jié)束時(shí),需要 注銷訂閱者。

舉例:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
2.4 步驟4:發(fā)布事件
注冊(cè)訂閱者之后,就可以發(fā)布事件了,目前有兩種事件:
- 調(diào)用
EventBus#post(Object)發(fā)布普通事件 - 調(diào)用
EventBus#postSticky(Object)發(fā)布粘性事件

舉例:
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
粘性事件 的特點(diǎn)如下:

舉例:
1、訂閱
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.message);
}
2、發(fā)布
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
3、獲取粘性事件
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
if(stickyEvent != null) {
4、移除粘性事件
EventBus.getDefault().removeStickyEvent(stickyEvent);
// do something.
}
5、移除粘性事件
MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
if(stickyEvent != null) {
// do something.
}
3. 編譯時(shí)索引
EventBus 3.x相較于EventBus 2.x最大的改良就是 編譯時(shí)索引,注解生成器的源碼可查看:EventBus 注解處理器源碼,具體描述如下:

為了生成編譯時(shí)索引,首先需要在build.gradle中配置索引文件,例如:
kapt {
arguments {
arg('eventBusIndex', 'com.have.a.good.MyEventBusAppIndex')
}
}
編譯時(shí),注解處理器 將解析@Subscribe注解修飾的方法,生成 索引類MyEventBusAppIndex.java。你需要做的是在運(yùn)行時(shí)構(gòu)建時(shí)添加索引,例如:
EventBus eventBus = EventBus.builder()
.addIndex(new MyEventBusAppIndex())
.build();
需要注意:索引類配置只對(duì)當(dāng)前 module 有效,因此需要在每個(gè)包含訂閱者的 module 級(jí)build.gradle中添加索引類配置,例如:
// App module
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied
kapt {
arguments {
arg('eventBusIndex', 'com.have.a.good.MyEventBusAppIndex')
}
}
dependencies {
...
kapt "org.greenrobot:eventbus-annotation-processor:3.2.0"
implementation project(path: ':base')
}
// Lib module
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied
kapt {
arguments {
arg('eventBusIndex', 'com.have.a.good.MyEventBusLibIndex')
}
}
dependencies {
...
api 'org.greenrobot:eventbus:3.2.0'
kapt "org.greenrobot:eventbus-annotation-processor:3.2.0"
implementation project(path: ':base')
}
以上配置將生成兩個(gè)索引類文件,MyEventBusAppIndex.java只包含App Module中的訂閱者索引,而MyEventBusLibIndex.java只包含Lib Module中的訂閱者索引。
4. 構(gòu)建者模式
構(gòu)建者模式(Builder Pattern) 可以說是開源庫(kù)的標(biāo)配了,EventBus 也不例外。你可以使用EventBusBuilder來構(gòu)建 EventBus 實(shí)例,也可以直接調(diào)用EventBus.getDefault()獲得默認(rèn)的 EventBus 實(shí)例;
- 1、異常處理配置
| 配置項(xiàng) | 描述 | 默認(rèn)值 |
|---|---|---|
| logSubscriberExceptions | 訂閱函數(shù)執(zhí)行有異常時(shí),打印異常信息 | true |
| sendSubscriberExceptionEvent | 訂閱函數(shù)執(zhí)行有異常時(shí),發(fā)布SubscriberExceptionEvent事件 | true |
| throwSubscriberException | 訂閱函數(shù)執(zhí)行有異常時(shí),拋出SubscriberException | false |
| logNoSubscriberMessages | 事件無匹配訂閱函數(shù)時(shí),打印信息 | true |
| sendNoSubscriberEvent | 事件無匹配訂閱函數(shù)時(shí),發(fā)布NoSubscriberEvent | true |
- 2、索引配置
| 配置項(xiàng) | 描述 | 默認(rèn)值 |
|---|---|---|
| ignoreGeneratedIndex | 忽略訂閱者索引 | false |
| addIndex(SubscriberInfoIndex index) | 添加訂閱者索引 | 無 |
- 3、事件訂閱配置
| 配置項(xiàng) | 描述 | 默認(rèn)值 |
|---|---|---|
| eventInheritance | 是否觸發(fā)父類事件訂閱函數(shù) | true |
| executorService(ExecutorService executorService) | 線程池 | Executors# newCachedThreadPool() |
| strictMethodVerification | 是否嚴(yán)格驗(yàn)證訂閱函數(shù)簽名 | true |
| skipMethodVerificationFor(Class<?> clazz) | 跳過方法簽名驗(yàn)證 | 無 |
5. 混淆
ProGuard 和它的繼承者 R8 都提供了壓縮、優(yōu)化、混淆和預(yù)校驗(yàn)四大功能。壓縮和優(yōu)化會(huì)移除未使用的類/方法/字段,混淆會(huì)使用無意義的簡(jiǎn)短名稱重命名類/方法/字段。
@Subscribe 訂閱方法是通過反射調(diào)用的,在編譯時(shí)沒有直接調(diào)用,如果不增加反混淆規(guī)則的話,在運(yùn)行時(shí)會(huì)出現(xiàn)找不到方法名的情況。因此,EventBus需要配置以下混淆規(guī)則:
-keepattributes *Annotation*
// keep住所有被Subscribe注解標(biāo)注的方法
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
如果使用了AsyncExecutor,還需要配置混淆規(guī)則:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
更多內(nèi)容:《Android | 代碼混淆到底做了什么?》
6. 總結(jié)
EventBus 是一套 Android / Java 事件訂閱 / 發(fā)布框架,用于在組件 / 線程間通信的場(chǎng)景中將數(shù)據(jù)或事件傳遞給訂閱者,EventBus 的特點(diǎn)是可以實(shí)現(xiàn)事件訂閱者與發(fā)布者解耦,以及透明地線程切換;
EventBus 3.x 中,訂閱者需要使用 @Subscribe 注解修飾訂閱方法,可選五種線程模式(POSTING、MAIN、MAIN_ORDERED、BACKGROUND 和 ASYNC);
編譯時(shí)索引的原理是通過編譯時(shí)注解處理器生成索引表,記錄事件 —— 訂閱關(guān)系的映射,在運(yùn)行時(shí)直接加載索引表。如果不使用編譯時(shí)索引,在注冊(cè)訂閱者時(shí)就需要遞歸反射查找類本身與父類中使用@Subscribe注解修飾的方法,影響性能;
@Subscribe 訂閱方法是通過反射調(diào)用的,在編譯時(shí)沒有直接調(diào)用,如果不增加反混淆規(guī)則的話,在運(yùn)行時(shí)會(huì)出現(xiàn)找不到方法名的情況。
2020 永遠(yuǎn)不要放棄希望,祝愿大家都能夠平安健康!武漢加油!
