這篇文章主要記錄一下EventBus的使用,詳情EventBus官方文檔
1. 概念
EventBus能夠簡化各組件間的通信,讓我們的代碼書寫變得簡單,能有效的分離事件發(fā)送方和接收方(也就是解耦的意思),能避免復(fù)雜和容易出錯(cuò)的依賴性和生命周期問題。
事件(Event):又可稱為消息,本文中統(tǒng)一用事件表示。其實(shí)就是一個(gè)POJO對象
事件分為一般事件和 Sticky 事件,相對于一般事件,Sticky 事件不同之處在于,當(dāng)事件發(fā)布后,再有訂閱者開始訂閱該類型事件,依然能收到該類型事件最近一個(gè) Sticky 事件。訂閱者(Subscriber):訂閱某種事件類型的對象。當(dāng)有發(fā)布者發(fā)布這類事件后,EventBus 會(huì)執(zhí)行訂閱者的 @Subscribe注解標(biāo)記的方法,叫做事件響應(yīng)。訂閱者通過 register 接口訂閱某個(gè)事件類型,unregister 接口退訂。訂閱者存在優(yōu)先級,優(yōu)先級高的訂閱者可以取消事件繼續(xù)向優(yōu)先級低的訂閱者分發(fā)(但是取消事件的線程必須和發(fā)布事件的線程一致),默認(rèn)所有訂閱者優(yōu)先級都為 0。
發(fā)布者(Publisher):發(fā)布某事件的對象,通過 post 接口發(fā)布事件。
2. 簡單使用
2.1. 添加EventBus庫
api 'org.greenrobot:eventbus:3.1.1'
2.2. 新建一個(gè)POJO對象代表一個(gè)事件(Event)
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
2.3. 在生命周期里面注冊和取消注冊
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
2.4. 訂閱者處理事件
// 當(dāng) MessageEvent 事件被發(fā)布這個(gè)方法會(huì)被調(diào)用(在主線程中顯示一個(gè) Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
2.5. 發(fā)布事件
發(fā)布事件的類和訂閱者的類不相同時(shí),兩個(gè)類不直接引用就可以進(jìn)行通信.
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
3. 擴(kuò)展
3.1. ThreadMode (線程模式)
訂閱者響應(yīng)事件的方法是通過 @Subscribe 注解標(biāo)注的,其中有個(gè) ThreadMode 屬性可以進(jìn)行控制響應(yīng)事件方法的執(zhí)行線程,一共有 5 種線程模式可以進(jìn)行配置
-
ThreadMode: POSTING (默認(rèn))
訂閱者(Subscriber) 在 發(fā)布者(Publisher) post 事件相同的線程中執(zhí)行響應(yīng)事件的方法,若 發(fā)布者(Publisher) post 的線程是主線程,則 訂閱者(Subscriber) 處理事件的線程也在主線程中執(zhí)行,若 發(fā)布者(Publisher) post 事件的線程是子線程,則 訂閱者(Subscriber) 處理消息事件的線程也是在子線程中執(zhí)行.
// Called in the same thread (default)
// ThreadMode is optional here
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
log(event.message);
}
-
ThreadMode: MAIN
訂閱者(Subscriber) 在主線程中執(zhí)行響應(yīng)事件的方法,如果 發(fā)布者(Publisher) post 事件是主線程,則直接調(diào)用響應(yīng)事件的方法,如果 post 的是子線程,則加入到主線程的消息循環(huán)隊(duì)列中執(zhí)行響應(yīng)事件的方法,
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
-
ThreadMode: MAIN_ORDERED
訂閱者(Subscriber) 在主線程中執(zhí)行響應(yīng)事件的方法 和 ThreadMode: MAIN 區(qū)別在于,不管 發(fā)布者(Publisher) post 事件是什么線程 ,MAIN_ORDERED會(huì)把事件加入到主線程的消息循環(huán)隊(duì)列中執(zhí)行,而不會(huì)直接調(diào)用處理消息的方法
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
-
ThreadMode: BACKGROUND
訂閱者(Subscriber) 在子線程中執(zhí)行響應(yīng)事件的方法.若 發(fā)布者(Publisher) post 事件為主線程,則在后臺(tái)子線程中執(zhí)行.所有的 ThreadMode: BACKGROUND 事件要轉(zhuǎn)化在子線程處理的都共用一個(gè)相同的后臺(tái)子線程 ,若 發(fā)布者(Publisher) post 事件的線程為子線程,則就直接在 post 事件的線程中處理.
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}
-
ThreadMode: ASYNC
訂閱者(Subscriber) 在獨(dú)立的子線程中執(zhí)行響應(yīng)事件的方法,既不是 發(fā)布者(Publisher) post 事件的線程,不是主線程,也不是 ThreadMode: BACKGROUND 的后臺(tái)子線程 .發(fā)布者(Publisher) post 事件的線程不會(huì)等待訂閱者(Subscriber) 處理事件的線程響應(yīng).適用于處理事件時(shí)間較長的情況
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}
3.2. EventBus 配置
EventBus eventBus = EventBus.builder()
.logSubscriberExceptions(false) // 默認(rèn)為 ture 是否記錄 調(diào)用訂閱者響應(yīng)事件的方法出現(xiàn)異常時(shí)的異常日志
.logNoSubscriberMessages(false) // 默認(rèn)為 ture 是否記錄 發(fā)布者(Publisher) 發(fā)布事件時(shí)沒有訂閱者(Subscriber) 的日志
.sendNoSubscriberEvent(false) // 默認(rèn)為 ture 當(dāng)發(fā)布者(Publisher) 發(fā)布事件時(shí)沒有訂閱者(Subscriber) 是否將事件轉(zhuǎn)化為 post 一個(gè) NoSubscriberEvent事件
.sendSubscriberExceptionEvent(false) // 默認(rèn)為 ture 調(diào)用訂閱者響應(yīng)事件的方法出現(xiàn)異常時(shí) 是否 post 一個(gè)SubscriberExceptionEvent 事件
.throwSubscriberException(BuildConfig.DEBUG) // 默認(rèn)為 false 調(diào)用訂閱者響應(yīng)事件的方法出現(xiàn)異常時(shí)是否拋出 EventBusException
.eventInheritance(false) // 默認(rèn)為 ture 若 發(fā)布者(Publisher) 發(fā)布的事件是訂閱者(Subscriber) 的訂閱事件的子類,是否將事件傳遞給訂閱者(Subscriber) 處理
.ignoreGeneratedIndex(true) //默認(rèn)為 false 是否忽略 Index
.strictMethodVerification(true) // 默認(rèn)為 false ,是否嚴(yán)格認(rèn)證 @Subscribe 注解標(biāo)注的訂閱者響應(yīng)事件方法,如果 方法是0個(gè)或多于1個(gè)參數(shù),或者是 非 public, 抽象的,靜態(tài)的,會(huì)拋出 EventBusException
.installDefaultEventBus();
3.3. Sticky 事件
Sticky 事件不同之處在于,當(dāng)事件發(fā)布后,再有訂閱者開始訂閱該類型事件,依然能收到該類型事件最近一個(gè) Sticky 事件。
-
訂閱Sticky事件
// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.message);
}
-
發(fā)布Sticky事件
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
-
移除Sticky事件
Sticky事件 在事件發(fā)布之后依然會(huì)接收的到,所以不會(huì)丟失,若不需要時(shí)必須手動(dòng)移除
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// "Consume" the sticky event
EventBus.getDefault().removeStickyEvent(stickyEvent);
// Now do something with it
}
移除 Sticky事件 還有一個(gè)重載的方法
MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// Now do something with it
}
3.4. 優(yōu)先級和取消事件傳遞
-
優(yōu)先級
默認(rèn)優(yōu)先級是 0,高優(yōu)先級的的訂閱者先接收到事件.只有相同在ThreadMode 下才能比較優(yōu)先級 不同的 ThreadMode下的訂閱者的優(yōu)先級別不起作用
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
...
}
-
取消事件傳遞
只有 和 發(fā)布者(Publisher) post 事件相同的線程的 訂閱者(Subscriber) 才能取消事件傳遞,不然的話會(huì)報(bào)異常
/ Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
// Process the event
...
// Prevent delivery to other subscribers
EventBus.getDefault().cancelEventDelivery(event) ;
}
3.5. Index(索引)
Index是 EventBus 3 上添加的新特性,默認(rèn)是用使用反射,而 Index 是編譯時(shí) 使用 annotationProcessor 生成輔助的 SubscriberInfoIndex 類 ,里面會(huì)記錄訂閱者信息,就不用反射掃描類中方法的,所以 Android上推薦使用 Index ,效率更高
-
使用 annotationProcessor 生成 Index
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
}
}
}
}
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
-
使用 index
配置好 annotationProcessor 后使用 index 和之前的唯一區(qū)別是 EventBus.getDefault()之前必須使用 addIndex 方法進(jìn)行初始化
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
3.6. AsyncExecutor 輔助類
AsyncExecutor是一個(gè)輔助類會(huì)創(chuàng)建一個(gè)線程池,在 RunnableEx 里面執(zhí)行出現(xiàn)異常會(huì)被捕獲不用自己處理,并被轉(zhuǎn)化為一個(gè) ThrowableFailureEvent事件并 post 出去
AsyncExecutor.create().execute(
new AsyncExecutor.RunnableEx() {
@Override
public void run() throws LoginException {
// No need to catch any Exception (here: LoginException)
remote.login();
EventBus.getDefault().postSticky(new LoggedInEvent());
}
}
);
@Subscribe(threadMode = ThreadMode.MAIN)
public void handleLoginEvent(LoggedInEvent event) {
// do something
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void handleFailureEvent(ThrowableFailureEvent event) {
// do something
}