EventBus源碼分析(一)
EventBus官方介紹為一個為Android系統優化的事件訂閱總線,它不僅可以很方便的在同線程中傳遞事件或者對象,還可以在不同線程中實現事件或對象的傳遞,用法比較簡單,可以很好地完成一些在原生系統中的Intent,Handler等可以完成的工作,在Android開發過程中用途及其廣泛,這里不再介紹它的具體用法,由于在工作中遇到過一些關于EventBus的問題,所以計劃學習一下EventBus的設計與實現。接下來的幾篇文章記錄對于EventBus源碼的學習過程,閱讀其源碼,不僅可以在遇到問題時可以很快找到原因,更重要的是發現EventBus源碼程序中的類型結構層次以及數據結構的設計有很多值得我們借鑒的地方。
EventBus的整體結構是典型的觀察者模式,其工作流程如下:
在傳統的觀察者模式中,被觀察者記錄訂閱者的信息,在事件發生以后依次調用訂閱者的相應方法。EventBus對其做了擴展,將訂閱信息的管理和事件的分發調度從被觀察者中抽離出來,就是我們的EventBus, 它作為一個事件總線,被觀察者在事件發生后可以將事件推送到事件總線上,EventBus負責在合適的時機調用訂閱者的相應方法。由于EventBus的存在,被觀察者和訂閱者分離,所以我們的程序中使用EventBus傳遞事件或對象及其簡單,中間過程全交由EventBus處理,本篇文章的重點就是關注EventBus在中間所做的事情。簡單來說,EventBus的任務就是傳統觀察者當中被觀察者的管理注冊信息和調度方法兩項任務。下面我們從EventBus這個類的源碼開始分析,首先從構造器開始。
1. 構造器
EventBus的構造方式是典型的建造者模式,首先看我們獲取EventBus最常用的方法,getDefault()
/** 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即可,這樣方便事件的處理,同時EventBus對事件的注冊和調度方法都是線程安全的,我們在后面就會看到,所以單例非常適合。接下來看默認構造器:
public EventBus() {
this(DEFAULT_BUILDER);
}
DEFAULT_BUILDER是一個靜態的構造者,其中包含一系列的默認屬性,最后就是以建造者為參數的構造器,過程就是一系列的參數初始化,代碼如下:
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>(); //以事件類的class對象為鍵值,記錄注冊方法信息,值為一個Subscription的列表
typesBySubscriber = new HashMap<>(); //以注冊的類為鍵值,記錄該類所注冊的所有事件類型,值為一個Event的class對象的列表
stickyEvents = new ConcurrentHashMap<>(); //記錄sticky事件
//三個Poster, 負責在不同的線程中調用訂閱者的方法
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
...
//方法的查找類,用于查找某個類中有哪些注冊的方法
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
...
//后面是一些表示開關信息的boolean值以及一個線程池
}
這里省略了一部分暫時沒有分析到的參數。首先來看管理注冊信息的兩個Map, 由于一個類中可能有多個方法監聽多個事件,所以Subscription這個類封裝一個注冊信息,這個類很簡單,只有三個屬性,如下:
final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
/**
* Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
* {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
*/
volatile boolean active;
//之后為構造器以及覆寫的equal和hashCode方法。
...
}
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;
//之后為構造器以及覆寫的equal和hashCode方法。
...
所以類和方法唯一確定一條注冊信息,active表示該注冊信息是否有效(如注釋所說它的作用)。所以在EventBus中,使用subscriptionsByEventType,以Event的class對象為鍵值,管理注冊信息,值為一個處理事件類型為該鍵值的Subscription的列表。typesBySubscriber則相對簡單,就是記錄一個類注冊了哪些事件類型。雖然二者有所冗余,它們在后面的注冊和調度過程中都是為了便于查詢,這樣更為高效。
然后是stickyEvents,是一個線程安全的Map,用來記錄sticky事件,sticky事件的含義是指即使被觀察者發送sticky事件是在訂閱者訂閱該事件之前,訂閱者在訂閱之后,EventBus將該事件發送到該訂閱者,即調用相應的訂閱方法。(如果在之后,那就和普通事件一樣)
接下來的三個Poster極為重要,但是這里理解很容易,就是負責在不同線程中調用方法,他們分別對應著threadMode中除去POSTING之外三種類型。
最后是一個Finder,由于在EventBus的使用簡便,都是以對象為單位調用registe()方法,但是一個對象中可能有多個注冊方法,所以注冊過程中需要subscriberMethodFinder查找一個類中有哪些注冊方法,最后生成一個Subscrip,并存在subscriptionsByEventType的某一個列表中。
EventBus的構造器很簡單,這里重點講述了一下其中的幾個比較重要的成員變量,尤其是管理注冊信息的數據結構,下一步就是注冊源碼的分析。
2. 注冊
注冊方法rigister()的代碼如下:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
思路很明確,兩個步驟,一是查找注冊方法的列表,這個利用到了SubscriberMethodFinder的對應方法,查找一個類中有哪些注冊方法,而是調用訂閱方法,參數為類和方法兩個,即subscriber和subscriberMethod。查找我們留到下一部分,先看訂閱方法:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
代碼雖然有點長,但是邏輯很簡單清晰,就是構建Subscription, 然后將該注冊信息保存到兩個數據結構中。對于subscriptionsByEventType首先獲取EventType對應的列表,沒有則創建,重復注冊則拋異常,正常情況下,則根據priority插入到列表中適合的位置。對于typesBySubscriber,則是更新該subscriber對應的列表即可。最后是處理sticky事件,即在注冊時,如果是監聽sticky事件,則需要從stickyEvents中取出對應sticky事件,并發送到訂閱者。這里需要注意eventInheritance是一個開關,表示是否處理EventType的繼承關系,默認為true,如代碼中,EventBus會向訂閱者發送該類型的事件,以及該類型所有子類類型的事件。由于訂閱者監聽一個sticky事件,那么該sticky事件的子類型也可以認為是該類型的事件,所以訂閱者也同樣會接收到該事件。最后checkPostStickyEventToSubscription方法如下:
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
代碼很簡單,至于如何發送,放到第三部分統一分析,注釋中說的訂閱者不能終止sticky事件的發送,至于posting state則同樣放在但三部分說明。
注冊過程就是這些,還有一個重要且稍微復雜的查找過程放在第二篇中分析,在分析事件分發之前,與注冊相反的unrigister由于比較簡單,這里一并說了,下面是其代碼:
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
邏輯很明確,調用unsubscribeByEventType方法,并更新typesBySubscriber數據結構,那么你可能猜到了,unsubscribeByEventType方法中就是更新另一個數據結構,代碼如下:
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
這里邏輯很簡單,但是有一點值得注意的是就遍歷刪除列表時,注意序號i和size的改變,容易出現數組越界的錯誤,遍歷刪除通常使用倒序的方式,不容易出現錯誤,這里動態改變size變量也是一樣。注銷過程需要考慮sticky事件,也不需要查找過程,所以過程很簡單。
register和unregister的過程除了查找訂閱方法以外,邏輯很簡單,就是更新一下管理注冊信息的數據結構,可以看出數據結構的設計還是很重要的,設計的好,邏輯就會很清晰,程序代碼也就相對簡潔易懂。下一步就是介紹事件的分發。
3. 事件分發
在分析post()方法之前,先看EventBus的一個內部類,PostState:
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
這個內部類只有幾個屬性變量,其中我們現在只需要注意第一個,事件消息隊列即可。如注釋所言,這個類的實例對象在EventBus中是一個ThreadLocal變量,即線程本地變量,不同線程之間不會相互影響,而eventQueue則是用來保存當前線程需要發送的事件(為什么會有隊列,是因為POST線程也就是調用post()方法的線程與調用訂閱者方法的線程不同,在POST線程中連續的調用post()方法發送事件,會造成事件的累積)。后面的四個變量都是與cancelEventDelivery()方法有關,在后面對其進行分析。
下面為post()方法的代碼:
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
首先需要明確的是,如注釋所言,post()的方法是將事件發送到EventBus,至于何時調用訂閱者的方法則有EventBus調度。從代碼中看postingState是一個ThreadLocal,用于保存當前post事件的狀態。post()方法就是設置一些postState的屬性,然后遍歷事件消息隊列,調用postSingleEvent()方法,其代碼如下:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
這個方法中同樣也用到了eventInheritance這個開關,即是否考慮Event事件類型的繼承關系,默認為true,這里的lookupAllEventTypes()方法是EventBus的靜態方法,查找eventClass所有包括自己在內的父類以及它們所實現的接口,然后對于沒有eventClass,調用postSingleForEventType()方法,返回的結果為是否找到了對應的訂閱方法,在沒有找到的情況下,會做出打印Log信息和發送事件處理,這里的logNoSubscriberMessages和sendNoSubscriberEvent是EventBus的開關屬性,與eventInheritance類似,也可以在Builder中設置,默認為true。如果當我們調用post()方法發出某個事件時想知道我們的事件有沒有被訂閱者接收,就可以在發送消息的類中接收NoSubscriberEvent事件,如果收到該事件說明應用中沒有訂閱者接收我們發出的事件。
這里在看源碼時有一點點小疑問,就是在注冊時處理sticky事件時是找到Event的所有子類并發送給該訂閱者,而這里是Event的所有父類,并將其發送出去。前者是站在訂閱者的角度上,訂閱者在注冊時要求接收某個sticky事件,那么該事件的所有子類也是該sticky事件的一種,所以應該發送給該訂閱者。比如一個訂閱者訂閱天氣預報的sticky事件,那么如果在stickyEvent中有一個今天下雨的事件(假設該事件繼承自天氣預報,隨便想的例子,可能不太恰當)也應該發送給該訂閱者。而此處postSingleEvent方法中是發送單個Event,是站在發送者發送到EventBus的角度上,我需要發送某個事件通知訂閱者,比如我發送一個天氣預報的事件到EventBus, 但是EventBus由于不知道天氣預報是否表示下雨,就不應該通知哪些是否下雨事件的訂閱者,相反那些監聽新聞的訂閱者(假設天氣預報繼承自新聞,而天氣預報是一條新聞),EventBus需要負責通知他們這條天氣預報的新聞。被觀察者沒有將新聞發送到EventBus上,但是EventBus則因為一條天氣預報需要找出新聞并發送到相應的訂閱者上,所以發送是兩個過程。接下來是postSingleEventForEventType方法,其代碼如下:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
這里首先說明CopyOnWriteArrayList是為了線程安全,每次對List的修改都會重新一份,由于是線程安全的所以不需要同步處理,但是對HashMap的讀取操作則不是線程安全的,所以需要線程同步。這個方法的邏輯也很簡單,就是從subscriptionsByEventType中找出事件類型對應的注冊信息列表,然后遍歷調用postToSubscription()方法,這個方法有些熟悉了,就是在注冊中處理sticky事件時調用的方法。不過這里需要注意的是abort每次都會讀取postingState的cancel狀態判斷發送事件是否被終止,而另外兩個遍歷 event和subscription的賦值和清空也是為了用于cancelEventDelivery()方法,后面會統一說。最后終于到了postToSubscription()方法,其代碼為:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
這里就到了真正的事件分發了,分為我們所熟知的四種threadModed的情形,其代碼邏輯很清晰,根據threadMode以及isMainThread選擇調用invokeSubscriber方法還是加入相應的隊列,異步執行。關于入隊異步執行放在第三篇中講述,重點分析三個Poster的實現,這里先貼出invokeSubscriber的代碼,其實就是利用反射調用訂閱者的方法,方法存儲在subsription的subsribeMethod變量中:
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
到這里事件的分發就結束了,梳理一下就是一下流程:1. post(Object event) -> 2. postSingleEvent(Object event, PostingThreadState postingState) -> 3. postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) -> 4. postToSubscription(Subscription subscription, Object event, boolean isMainThread).
其中第一步是遍歷一個線程本地變量中保存的事件消息隊列的所有消息,第二步是遍歷一個事件類型的所有父類,第三步是遍歷一個事件類型的所有注冊信息,第四步則是事件分發,根據threadMode選擇合適的處理方式。
讀到這里就感覺優秀代碼的確是邏輯結構十分清晰,看起來一目了然,在一個任務中合理的劃分步驟,拆分成多個方法,讓人更容易理解。
接下來就是前面一直提到的cancelEventDelivery方法:
/**
* Called from a subscriber's event handling method, further event delivery will be canceled. Subsequent
* subscribers
* won't receive the event. Events are usually canceled by higher priority subscribers (see
* {@link Subscribe#priority()}). Canceling is restricted to event handling methods running in posting thread
* {@link ThreadMode#POSTING}.
*/
public void cancelEventDelivery(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
if (!postingState.isPosting) {
throw new EventBusException(
"This method may only be called from inside event handling methods on the posting thread");
} else if (event == null) {
throw new EventBusException("Event may not be null");
} else if (postingState.event != event) {
throw new EventBusException("Only the currently handled event may be aborted");
} else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) {
throw new EventBusException(" event handlers may only abort the incoming event");
}
postingState.canceled = true;
}
這里重點先看一下注釋,這個方法的作用就是在事件處理方法中調用,終止事件的進一步傳遞,這個和android系統中的順序廣播是相同的道理(即通過提高自己的的優先權可以率先收到事件,然后截取該事件)。但是這里有一個條件就是事件處理方法的threadMode必須是POSTING,即執行線程與事件的發送線程為同一個線程。
從代碼中可以看出終止事件條件十分苛刻,PostState中的三個變量都是用來判斷是否可以終止事件的,第一個是isPosting,該變量在post方法中被設置true,表示一個事件類型及其父類正在被發送的狀態中,發送完畢以后才被設置為false。所以在這個過程以外都不可以調用該方法。最后兩個條件postSingleEventForEventType中被設置的,對于一個事件類型(不包括其父類)遍歷它的所有注冊信息,針對每一個注冊信息調用postToSubscription方法之前和之后這兩個變量都會被設置,就是為了在這里判斷,是否可以在某一個注冊信息的方法被調用時終止這個事件的繼續發送。(這里有一點不明白,一個事件的所有注冊信息遍歷時,事件是同一個,為什么在遍歷過程中都要賦值和清空,這里是否可以改成設置一次和清空一次即可?)。最后在設置了canceled變量以后(如果cancelEventDelivery在事件處理方法中被調用了),事件處理方法返回之后,postToSubscription接著返回,canceled被賦值到了abort變量,這時候abort如果為true, 則break跳出循環,從而終止了該事件類型的繼續發送到其他的注冊信息。(注意這里不會影響其他事件類型,如其父類事件等)
看到這里也就明白了為什么限制在threadMode為POSTING的事件處理方法中調用cancelEventDelivery方法了,這是因為post()之后的一系列方法是在事件發送的線程中執行,而這些狀態字的賦值與判斷必須處在同一線程中才能有效,所以事件處理方法必須與post()方法處在同一線程,所以也就只能是POSTING模式下才能保證。
最后invokeSubscriber方法中在調用訂閱者方法失敗時有一個異常處理方法,其代碼如下:
private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {
if (event instanceof SubscriberExceptionEvent) {
if (logSubscriberExceptions) {
// Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log
Log.e(TAG, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass()
+ " threw an exception", cause);
SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;
Log.e(TAG, "Initial event " + exEvent.causingEvent + " caused exception in "
+ exEvent.causingSubscriber, exEvent.throwable);
}
} else {
if (throwSubscriberException) {
throw new EventBusException("Invoking subscriber failed", cause);
}
if (logSubscriberExceptions) {
Log.e(TAG, "Could not dispatch event: " + event.getClass() + " to subscribing class "
+ subscription.subscriber.getClass(), cause);
}
if (sendSubscriberExceptionEvent) {
SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event,
subscription.subscriber);
post(exEvent);
}
}
}
代碼邏輯很簡單,通常由于是發送的是我們自定義的Event, 所以會走else, 接著就會根據開關拋異常打log以及發送事件等,讀一下這段代碼也有利于我們以后調試有關EventBus的相關問題。
至此事件分發邏輯就分析結束了。下一步簡單介紹和sticky事件相關的幾個方法。
4. sticky事件
在注冊部分我們提到過sticky事件,即在訂閱時即可以接收到之前post出去的sticky事件以及其子類事件,下面為postSticky()方法:
/**
* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
*/
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
之前可能對于sticky的解釋不太清楚,不過這個方法的注釋則對sticky事件的解釋很清晰,淺顯易懂。接著需要注意方法中的那句注釋,就是需要先將sticky事件保存到stickyEvents中在調用post()方法,是為了防止remove失敗,如在事件處理方法中調用removeStickyEvent,remove在put之前則會造成remove失敗,post方法返回以后event又被添加到stickyEvents,與我們期望的就有差別了。接下來是sticky事件的remove方法:
/**
* Remove and gets the recent sticky event for the given event type.
*
* @see #postSticky(Object)
*/
public <T> T removeStickyEvent(Class<T> eventType) {
synchronized (stickyEvents) {
return eventType.cast(stickyEvents.remove(eventType));
}
}
/**
* Removes the sticky event if it equals to the given event.
*
* @return true if the events matched and the sticky event was removed.
*/
public boolean removeStickyEvent(Object event) {
synchronized (stickyEvents) {
Class<?> eventType = event.getClass();
Object existingEvent = stickyEvents.get(eventType);
if (event.equals(existingEvent)) {
stickyEvents.remove(eventType);
return true;
} else {
return false;
}
}
}
/**
* Removes all sticky events.
*/
public void removeAllStickyEvents() {
synchronized (stickyEvents) {
stickyEvents.clear();
}
}
這幾個方法都比較容易理解,不再解釋了,最后關于sticky還有一個get方法,如下:
/**
* Gets the most recent sticky event for the given type.
*
* @see #postSticky(Object)
*/
public <T> T getStickyEvent(Class<T> eventType) {
synchronized (stickyEvents) {
return eventType.cast(stickyEvents.get(eventType));
}
}
最后貼出來兩個靜態方法,就是前面提到的查找事件類型和它所有父類以及它們的實現的所有接口的方法,對于過程的分析無關緊要,有興趣的可以看一下源碼,如下:
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
/** Recurses through super interfaces. */
static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
for (Class<?> interfaceClass : interfaces) {
if (!eventTypes.contains(interfaceClass)) {
eventTypes.add(interfaceClass);
addInterfaces(eventTypes, interfaceClass.getInterfaces());
}
}
}
到這里,EventBus這個類的代碼幾乎全部分析結束了,還有幾個方法會在其他地方用到,不會影響這個注冊和分發的流程,所以這里不再貼出其源碼了,有興趣的可以自行查看。
后記
本篇文章主要分析了EventBus這個類,分析了EventBus基于建造者模式的構造器以及整個流程中的注冊訂閱者和事件分發的流程,重點是EventBus中設計的數據結構,以及在過程中對這些數據結構的操作,從中有很多的借鑒意義。在這整個流程中還有兩個重要的步驟,一是查找所有訂閱方法的信息,使用反射查找方法信息較為簡單,由于在EventBus3.0之后使用了注解,訂閱方法的名字不再限制為onEvent(),編程更為方便,但是需要對訂閱方法的查詢,反射由于處于運行期影響性能,所以EventBus選擇在編譯期處理注解,查詢訂閱方法信息,這部分在下一篇文章中做分析。第二個沒有分析的重要的步驟就是三個Poster的實現,即如何在不同線程中發送事件,我們都知道android的異步事件都是通過handler處理,EventBus也是基于Handler進行異步處理,但是EventBus使用明顯方便太多,這一切都歸功于Poster,對于Poster的分析則放在第三篇文章中分析。