EventBus是一個 發(fā)布/訂閱 模式的消息總線庫,它簡化了應用程序內各組件間、組件與后臺線程間的通信,解耦了事件的發(fā)送者和接收者,避免了復雜的、易于出錯的依賴及生命周期問題,可以使我們的代碼更加簡潔、健壯。
在不使用EventBus的情況下,我們也可能會使用諸如 Observable/Observer 這樣得一些機制來處理事件的監(jiān)聽/發(fā)布。如果在我們的應用程序中,有許多地方需要使用事件的監(jiān)聽/發(fā)布,則我們應用程序的整個結構可能就會像下面這個樣子:

每一處需要用到事件的
監(jiān)聽/發(fā)布的地方,都需要實現(xiàn)一整套監(jiān)聽/發(fā)布的機制,比如定義Listener接口,定義Notification Center/Observable,定義事件類,定義注冊監(jiān)聽者的方法、移除監(jiān)聽者的方法、發(fā)布事件的方法等。我們不得不寫許多繁瑣的,甚至常常是重復的冗余的代碼來實現(xiàn)我們的設計目的。
引入EventBus庫之后,事件的監(jiān)聽/發(fā)布將變得非常簡單,我們應用程序的結構也將更加簡潔,會如下面這樣:

我們可以將事件監(jiān)聽者的管理,注冊監(jiān)聽者、移除監(jiān)聽者,事件發(fā)布等方法等都交給EventBus來完成,而只定義事件類,實現(xiàn)事件處理方法即可。
在使用Observable/Observer來實現(xiàn)事件的監(jiān)聽/發(fā)布時,監(jiān)聽者和事件發(fā)布者之間的關聯(lián)關系非常明晰,事件發(fā)布者找到事件的監(jiān)聽者從不成為問題。引入EventBus之后,它會統(tǒng)一管理所有對不同類型事件感興趣的監(jiān)聽者,則事件發(fā)布者在發(fā)布事件時,能夠準確高效地找到對事件感興趣的監(jiān)聽者將成為重要的問題。后面我們就通過對EventBus代碼實現(xiàn)的分析,來獲取這樣一些重要問題的答案,同時來欣賞EventBus得設計。
EventBus的基本使用
在具體分析EventBus的代碼之前,我們先來了解一下EventBus的用法。想要使用EventBus,首先需要在我們應用程序的Gradle腳本中添加對于EventBus的依賴,以當前最新版3.0.0為例:
compile 'org.greenrobot:eventbus:3.0.0'
然后,我們需要定義事件。事件是POJO(plain old Java object),并沒有特殊的要求:
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
其次,準備訂閱者,訂閱者實現(xiàn)事件處理方法(也稱為“訂閱者方法”)。當事件被發(fā)布時,這些方法會被調用。在EventBus 3中,它們通過 @Subscribe annotation進行聲明,方法名可以自由地進行選擇。而在EventBus 2中,則是通過命名模式來聲明監(jiān)聽者方法的。
// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
doSomethingWith(event);
}
訂閱者需要向EventBus注冊和注銷它自己。只有當訂閱者注冊了,它們才能收到事件。在Android中,Activities和Fragments通常根據(jù)它們的生命周期來進行綁定:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
最后即是事件源發(fā)布事件。我們可以在代碼的任何位置發(fā)布事件,當前所有事件類型與發(fā)布的事件類型匹配的訂閱者都將收到它。
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
EventBus 更多特性說明
了解了EventBus的基本用法之后,我們再來了解一下它提供的一些高級特性,以方便我們后續(xù)理解它的設計決策。
Sticky事件
某些事件攜帶的信息在事件被發(fā)布之后依然有價值。比如,一個指示初始化過程完成的事件信號?;蛘呷绻阌袀鞲衅骰蛭恢脭?shù)據(jù),你想要獲取它們的最新值的情況。不是實現(xiàn)自己的緩存,而是使用sticky事件。EventBus將在內存中保存最新的某一類型的sticky事件。Sticky事件可以被發(fā)送給訂閱者,也可以顯式地查詢。從而使你可以不需要特別的邏輯來考慮已經(jīng)可用的數(shù)據(jù)。這與Android中的sticky broadcast有些類似。
比如,一個sticky事件在一段時間之前被拋出:
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
現(xiàn)在,sticky事件被拋出后的某個時刻,一個新的Activity啟動了。在注冊階段所有的sticky訂閱者方法將立即獲得之前拋出的sticky事件:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
// UI updates must run on MainThread
textField.setText(event.message);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
正如前面看到的,最新的sticky事件會在注冊時立即自動地被傳遞給匹配的訂閱者。但有時手動地去檢查sticky事件可能會更方便一些。有時也可能需要移除(消費)sticky事件以便于它不會再被傳遞。比如:
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
}
removeStickyEvent方法是重載的:當你傳遞class時,它將返回持有的之前的sticky事件。使用如下的變體,我們可以改進前面的例子:
MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// Now do something with it
}
優(yōu)先級及事件取消
盡管使用EventBus的大多數(shù)場景不需要優(yōu)先級或事件的取消,但在某些特殊的場景下它們還是很方便的。比如,在app處于前臺時一個事件可能觸發(fā)某些UI邏輯,而如果app當前對用戶不可見則需要有不同的響應。
我們可以定制訂閱者的優(yōu)先級??梢酝ㄟ^在注冊期間給訂閱者提供一個優(yōu)先級來改變事件傳遞的順序。
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
…
}
在相同的傳遞線程(ThreadMode)內,高優(yōu)先級的訂閱者將先于低優(yōu)先級的訂閱者收到事件。默認的優(yōu)先級是0。優(yōu)先級不影響處于不同ThreadModes中的訂閱者的事件傳遞順序。
我們還可以取消事件的傳遞。我們可以在一個訂閱者的事件處理方法中通過調用cancelEventDelivery(Object event)來取消事件的傳遞進程。所有后續(xù)的事件傳遞將被取消:后面的訂閱者將無法接收到事件。
// Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
// Process the event
…
EventBus.getDefault().cancelEventDelivery(event) ;
}
事件通常由高優(yōu)先級的訂閱者取消。取消被限制只能在發(fā)布線程ThreadMode.PostThread的事件處理方法中進行。
傳遞線程
EventBus可以幫忙處理線程:事件可以在不同于拋出事件的線程中傳遞。一個常見的使用場景是處理UI變化。在Android中,UI變化必須在UI(主)線程中完成。另一方面,網(wǎng)絡,或任何耗時任務,必須不運行在主線程。EventBus幫忙處理了那些任務并與UI線程同步(不需要深入了解線程事務,使用AsyncTask即可,等等)。
ThreadMode: POSTING
訂閱者將在與拋出事件相同的線程中被調用。這是默認的方式。事件傳遞是同步完成的,事件傳遞完成時,所有的訂閱者將已經(jīng)被調用一次了。這個ThreadMode意味著最小的開銷,因為它完全避免了線程的切換。對于已知耗時非常短的簡單的不需要請求主線程的任務,這是建議采用的模式。使用這個模式的事件處理器應該迅速返回以避免阻塞發(fā)布事件的線程,而后者可能是主線程。比如:
// Called in the same thread (default)
@Subscribe(threadMode = ThreadMode.POSTING) // ThreadMode is optional here
public void onMessage(MessageEvent event) {
log(event.message);
}
ThreadMode: MAIN
訂閱者將在Android的主線程中被調用(有時被稱為UI線程)。如果發(fā)布事件的線程是主線程,事件處理器方法將會直接被調用(如同ThreadMode.POSTING中描述的那樣)。事件處理器使用這個模式必須快速地返回以避免阻塞主線程。比如:
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
ThreadMode: BACKGROUND
訂閱者將在一個后臺線程中被調用。如果發(fā)布事件的線程不是主線程,事件處理器方法將直接在發(fā)布的線程中被調用。如果發(fā)布線程是主線程,EventBus使用一個單獨的后臺線程,它將順序地傳遞它所有的事件。使用這個模式的事件處理器應該嘗試快速返回以避免阻塞后臺線程。
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}
ThreadMode: ASYNC
事件處理器方法在另外一個線程中被調用。這總是獨立于發(fā)布事件的線程和主線程。發(fā)布線程從不等待使用這一模式的事件處理器。對于執(zhí)行比較耗時的事件處理器方法應該使用這個模式,比如對于網(wǎng)絡訪問。避免同時大量地觸發(fā)長時間運行的異步處理器方法而限制并發(fā)線程的數(shù)量。EventBus使用了一個線程池以有效地復用已完成異步事件處理器通知的線程。
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}
EventBus對象的創(chuàng)建
大體了解了EventBus提供的特性之后,我們來分析EventBus的設計與實現(xiàn)。我們從EventBus對象的創(chuàng)建開始。EventBus對象需要用EventBusBuilder來創(chuàng)建。以EventBus提供的default EventBus對象為例,我們來看一下創(chuàng)建的過程。
應用程序可以通過EventBus.getDefault()來獲取默認的EventBus對象:
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
雙重加鎖檢查手法來保證對EventBus.getDefault()調用的線程安全。來看EventBus的構造函數(shù):
public static EventBusBuilder builder() {
return new EventBusBuilder();
}
......
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
EventBus類提供了一個public的構造函數(shù),可以以默認的配置來構造EventBus對象。EventBusBuilder是繁雜的EventBus構造時所需配置信息的容器。至于利用EventBusBuilder進行配置的配置項具體的含義,暫時先不詳述。
package org.greenrobot.eventbus;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Creates EventBus instances with custom parameters and also allows to install a custom default EventBus instance.
* Create a new builder using {@link EventBus#builder()}.
*/
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
boolean logSubscriberExceptions = true;
boolean logNoSubscriberMessages = true;
boolean sendSubscriberExceptionEvent = true;
boolean sendNoSubscriberEvent = true;
boolean throwSubscriberException;
boolean eventInheritance = true;
boolean ignoreGeneratedIndex;
boolean strictMethodVerification;
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
List<Class<?>> skipMethodVerificationForClasses;
List<SubscriberInfoIndex> subscriberInfoIndexes;
EventBusBuilder() {
}
/** Default: true */
public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
this.logSubscriberExceptions = logSubscriberExceptions;
return this;
}
/** Default: true */
public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
this.logNoSubscriberMessages = logNoSubscriberMessages;
return this;
}
/** Default: true */
public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
return this;
}
/** Default: true */
public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
this.sendNoSubscriberEvent = sendNoSubscriberEvent;
return this;
}
/**
* Fails if an subscriber throws an exception (default: false).
* <p/>
* Tip: Use this with BuildConfig.DEBUG to let the app crash in DEBUG mode (only). This way, you won't miss
* exceptions during development.
*/
public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
this.throwSubscriberException = throwSubscriberException;
return this;
}
/**
* By default, EventBus considers the event class hierarchy (subscribers to super classes will be notified).
* Switching this feature off will improve posting of events. For simple event classes extending Object directly,
* we measured a speed up of 20% for event posting. For more complex event hierarchies, the speed up should be
* >20%.
* <p/>
* However, keep in mind that event posting usually consumes just a small proportion of CPU time inside an app,
* unless it is posting at high rates, e.g. hundreds/thousands of events per second.
*/
public EventBusBuilder eventInheritance(boolean eventInheritance) {
this.eventInheritance = eventInheritance;
return this;
}
/**
* Provide a custom thread pool to EventBus used for async and background event delivery. This is an advanced
* setting to that can break things: ensure the given ExecutorService won't get stuck to avoid undefined behavior.
*/
public EventBusBuilder executorService(ExecutorService executorService) {
this.executorService = executorService;
return this;
}
/**
* Method name verification is done for methods starting with onEvent to avoid typos; using this method you can
* exclude subscriber classes from this check. Also disables checks for method modifiers (public, not static nor
* abstract).
*/
public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
if (skipMethodVerificationForClasses == null) {
skipMethodVerificationForClasses = new ArrayList<>();
}
skipMethodVerificationForClasses.add(clazz);
return this;
}
/** Forces the use of reflection even if there's a generated index (default: false). */
public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
return this;
}
/** Enables strict method verification (default: false). */
public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
this.strictMethodVerification = strictMethodVerification;
return this;
}
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if(subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
/**
* Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be
* done only once before the first usage of the default EventBus.
*
* @throws EventBusException if there's already a default EventBus instance in place
*/
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
/** Builds an EventBus based on the current configuration. */
public EventBus build() {
return new EventBus(this);
}
}
這是一個蠻常規(guī)的Builder。我們看到了自定義可配置項創(chuàng)建EventBus對象的方法,也就是通過EventBusBuilder.build()方法。
EventBusBuilder還提供了一個方法installDefaultEventBus()可以讓我們在創(chuàng)建自定義EventBus對象時,將該對象設置為default EventBus對象。
總結一下EventBus對象創(chuàng)建的方式:
- 通過
EventBus的public不帶參數(shù)構造函數(shù),創(chuàng)建默認配置的EventBus對象。EventBus庫提供的defaultEventBus對象的創(chuàng)建方式。 - 通過
EventBusBuilder創(chuàng)建自定義配置的EventBus對象。這種方式的對象可以通過EventBusBuilder.installDefaultEventBus()在對象被創(chuàng)建的同時設置為defaultEventBus對象。
由此可見,引入EventBus庫之后,我們應用程序的結構實際上將如下面這樣:

EventBus所提供的特性就先介紹到這里。