EventBus源碼分析(一)
EventBus官方介紹為一個(gè)為Android系統(tǒng)優(yōu)化的事件訂閱總線烙无,它不僅可以很方便的在同線程中傳遞事件或者對(duì)象翼虫,還可以在不同線程中實(shí)現(xiàn)事件或?qū)ο蟮膫鬟f颂斜,用法比較簡(jiǎn)單,可以很好地完成一些在原生系統(tǒng)中的Intent,Handler等可以完成的工作福荸,在Android開發(fā)過程中用途及其廣泛肢础,這里不再介紹它的具體用法还栓,由于在工作中遇到過一些關(guān)于EventBus的問題,所以計(jì)劃學(xué)習(xí)一下EventBus的設(shè)計(jì)與實(shí)現(xiàn)传轰。接下來的幾篇文章記錄對(duì)于EventBus源碼的學(xué)習(xí)過程剩盒,閱讀其源碼,不僅可以在遇到問題時(shí)可以很快找到原因慨蛙,更重要的是發(fā)現(xiàn)EventBus源碼程序中的類型結(jié)構(gòu)層次以及數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)有很多值得我們借鑒的地方辽聊。
EventBus的整體結(jié)構(gòu)是典型的觀察者模式,其工作流程如下:
在傳統(tǒng)的觀察者模式中期贫,被觀察者記錄訂閱者的信息跟匆,在事件發(fā)生以后依次調(diào)用訂閱者的相應(yīng)方法。EventBus對(duì)其做了擴(kuò)展通砍,將訂閱信息的管理和事件的分發(fā)調(diào)度從被觀察者中抽離出來玛臂,就是我們的EventBus, 它作為一個(gè)事件總線烤蜕,被觀察者在事件發(fā)生后可以將事件推送到事件總線上,EventBus負(fù)責(zé)在合適的時(shí)機(jī)調(diào)用訂閱者的相應(yīng)方法迹冤。由于EventBus的存在讽营,被觀察者和訂閱者分離,所以我們的程序中使用EventBus傳遞事件或?qū)ο蠹捌浜?jiǎn)單泡徙,中間過程全交由EventBus處理橱鹏,本篇文章的重點(diǎn)就是關(guān)注EventBus在中間所做的事情。簡(jiǎn)單來說堪藐,EventBus的任務(wù)就是傳統(tǒng)觀察者當(dāng)中被觀察者的管理注冊(cè)信息和調(diào)度方法兩項(xiàng)任務(wù)蚀瘸。下面我們從EventBus這個(gè)類的源碼開始分析,首先從構(gòu)造器開始庶橱。
1. 構(gòu)造器
EventBus的構(gòu)造方式是典型的建造者模式贮勃,首先看我們獲取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;
}
如注釋所說苏章,這里是傳統(tǒng)的單例模式寂嘉,通常在一個(gè)應(yīng)用中使用同一個(gè)EventBus即可,這樣方便事件的處理枫绅,同時(shí)EventBus對(duì)事件的注冊(cè)和調(diào)度方法都是線程安全的泉孩,我們?cè)诤竺婢蜁?huì)看到,所以單例非常適合并淋。接下來看默認(rèn)構(gòu)造器:
public EventBus() {
this(DEFAULT_BUILDER);
}
DEFAULT_BUILDER是一個(gè)靜態(tài)的構(gòu)造者寓搬,其中包含一系列的默認(rèn)屬性,最后就是以建造者為參數(shù)的構(gòu)造器县耽,過程就是一系列的參數(shù)初始化句喷,代碼如下:
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>(); //以事件類的class對(duì)象為鍵值,記錄注冊(cè)方法信息兔毙,值為一個(gè)Subscription的列表
typesBySubscriber = new HashMap<>(); //以注冊(cè)的類為鍵值唾琼,記錄該類所注冊(cè)的所有事件類型,值為一個(gè)Event的class對(duì)象的列表
stickyEvents = new ConcurrentHashMap<>(); //記錄sticky事件
//三個(gè)Poster, 負(fù)責(zé)在不同的線程中調(diào)用訂閱者的方法
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
...
//方法的查找類澎剥,用于查找某個(gè)類中有哪些注冊(cè)的方法
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
...
//后面是一些表示開關(guān)信息的boolean值以及一個(gè)線程池
}
這里省略了一部分暫時(shí)沒有分析到的參數(shù)锡溯。首先來看管理注冊(cè)信息的兩個(gè)Map, 由于一個(gè)類中可能有多個(gè)方法監(jiān)聽多個(gè)事件,所以Subscription這個(gè)類封裝一個(gè)注冊(cè)信息哑姚,這個(gè)類很簡(jiǎn)單祭饭,只有三個(gè)屬性,如下:
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;
//之后為構(gòu)造器以及覆寫的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;
//之后為構(gòu)造器以及覆寫的equal和hashCode方法倡蝙。
...
所以類和方法唯一確定一條注冊(cè)信息,active表示該注冊(cè)信息是否有效(如注釋所說它的作用)宛乃。所以在EventBus中悠咱,使用subscriptionsByEventType,以Event的class對(duì)象為鍵值征炼,管理注冊(cè)信息析既,值為一個(gè)處理事件類型為該鍵值的Subscription的列表。typesBySubscriber則相對(duì)簡(jiǎn)單谆奥,就是記錄一個(gè)類注冊(cè)了哪些事件類型眼坏。雖然二者有所冗余,它們?cè)诤竺娴淖?cè)和調(diào)度過程中都是為了便于查詢酸些,這樣更為高效宰译。
然后是stickyEvents,是一個(gè)線程安全的Map,用來記錄sticky事件魄懂,sticky事件的含義是指即使被觀察者發(fā)送sticky事件是在訂閱者訂閱該事件之前沿侈,訂閱者在訂閱之后,EventBus將該事件發(fā)送到該訂閱者市栗,即調(diào)用相應(yīng)的訂閱方法缀拭。(如果在之后,那就和普通事件一樣)
接下來的三個(gè)Poster極為重要填帽,但是這里理解很容易蛛淋,就是負(fù)責(zé)在不同線程中調(diào)用方法,他們分別對(duì)應(yīng)著threadMode中除去POSTING之外三種類型篡腌。
最后是一個(gè)Finder褐荷,由于在EventBus的使用簡(jiǎn)便,都是以對(duì)象為單位調(diào)用registe()方法嘹悼,但是一個(gè)對(duì)象中可能有多個(gè)注冊(cè)方法叛甫,所以注冊(cè)過程中需要subscriberMethodFinder查找一個(gè)類中有哪些注冊(cè)方法,最后生成一個(gè)Subscrip杨伙,并存在subscriptionsByEventType的某一個(gè)列表中合溺。
EventBus的構(gòu)造器很簡(jiǎn)單,這里重點(diǎn)講述了一下其中的幾個(gè)比較重要的成員變量缀台,尤其是管理注冊(cè)信息的數(shù)據(jù)結(jié)構(gòu)棠赛,下一步就是注冊(cè)源碼的分析。
2. 注冊(cè)
注冊(cè)方法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);
}
}
}
思路很明確膛腐,兩個(gè)步驟睛约,一是查找注冊(cè)方法的列表,這個(gè)利用到了SubscriberMethodFinder的對(duì)應(yīng)方法哲身,查找一個(gè)類中有哪些注冊(cè)方法辩涝,而是調(diào)用訂閱方法,參數(shù)為類和方法兩個(gè)勘天,即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);
}
}
}
代碼雖然有點(diǎn)長(zhǎng),但是邏輯很簡(jiǎn)單清晰商膊,就是構(gòu)建Subscription, 然后將該注冊(cè)信息保存到兩個(gè)數(shù)據(jù)結(jié)構(gòu)中伏伐。對(duì)于subscriptionsByEventType首先獲取EventType對(duì)應(yīng)的列表,沒有則創(chuàng)建晕拆,重復(fù)注冊(cè)則拋異常藐翎,正常情況下,則根據(jù)priority插入到列表中適合的位置实幕。對(duì)于typesBySubscriber吝镣,則是更新該subscriber對(duì)應(yīng)的列表即可。最后是處理sticky事件昆庇,即在注冊(cè)時(shí)末贾,如果是監(jiān)聽sticky事件,則需要從stickyEvents中取出對(duì)應(yīng)sticky事件整吆,并發(fā)送到訂閱者未舟。這里需要注意eventInheritance是一個(gè)開關(guān),表示是否處理EventType的繼承關(guān)系掂为,默認(rèn)為true裕膀,如代碼中,EventBus會(huì)向訂閱者發(fā)送該類型的事件勇哗,以及該類型所有子類類型的事件昼扛。由于訂閱者監(jiān)聽一個(gè)sticky事件,那么該sticky事件的子類型也可以認(rèn)為是該類型的事件欲诺,所以訂閱者也同樣會(huì)接收到該事件抄谐。最后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());
}
}
代碼很簡(jiǎn)單,至于如何發(fā)送扰法,放到第三部分統(tǒng)一分析蛹含,注釋中說的訂閱者不能終止sticky事件的發(fā)送,至于posting state則同樣放在但三部分說明塞颁。
注冊(cè)過程就是這些浦箱,還有一個(gè)重要且稍微復(fù)雜的查找過程放在第二篇中分析,在分析事件分發(fā)之前祠锣,與注冊(cè)相反的unrigister由于比較簡(jiǎn)單酷窥,這里一并說了,下面是其代碼:
/** 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());
}
}
邏輯很明確伴网,調(diào)用unsubscribeByEventType方法蓬推,并更新typesBySubscriber數(shù)據(jù)結(jié)構(gòu),那么你可能猜到了澡腾,unsubscribeByEventType方法中就是更新另一個(gè)數(shù)據(jù)結(jié)構(gòu)沸伏,代碼如下:
/** 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--;
}
}
}
}
這里邏輯很簡(jiǎn)單糕珊,但是有一點(diǎn)值得注意的是就遍歷刪除列表時(shí),注意序號(hào)i和size的改變毅糟,容易出現(xiàn)數(shù)組越界的錯(cuò)誤红选,遍歷刪除通常使用倒序的方式,不容易出現(xiàn)錯(cuò)誤留特,這里動(dòng)態(tài)改變size變量也是一樣纠脾。注銷過程需要考慮sticky事件玛瘸,也不需要查找過程蜕青,所以過程很簡(jiǎn)單。
register和unregister的過程除了查找訂閱方法以外糊渊,邏輯很簡(jiǎn)單右核,就是更新一下管理注冊(cè)信息的數(shù)據(jù)結(jié)構(gòu),可以看出數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)還是很重要的渺绒,設(shè)計(jì)的好贺喝,邏輯就會(huì)很清晰,程序代碼也就相對(duì)簡(jiǎn)潔易懂宗兼。下一步就是介紹事件的分發(fā)躏鱼。
3. 事件分發(fā)
在分析post()方法之前,先看EventBus的一個(gè)內(nèi)部類殷绍,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;
}
這個(gè)內(nèi)部類只有幾個(gè)屬性變量染苛,其中我們現(xiàn)在只需要注意第一個(gè),事件消息隊(duì)列即可主到。如注釋所言茶行,這個(gè)類的實(shí)例對(duì)象在EventBus中是一個(gè)ThreadLocal變量,即線程本地變量登钥,不同線程之間不會(huì)相互影響畔师,而eventQueue則是用來保存當(dāng)前線程需要發(fā)送的事件(為什么會(huì)有隊(duì)列,是因?yàn)镻OST線程也就是調(diào)用post()方法的線程與調(diào)用訂閱者方法的線程不同牧牢,在POST線程中連續(xù)的調(diào)用post()方法發(fā)送事件看锉,會(huì)造成事件的累積)。后面的四個(gè)變量都是與cancelEventDelivery()方法有關(guān)塔鳍,在后面對(duì)其進(jìn)行分析度陆。
下面為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()的方法是將事件發(fā)送到EventBus懂傀,至于何時(shí)調(diào)用訂閱者的方法則有EventBus調(diào)度。從代碼中看postingState是一個(gè)ThreadLocal,用于保存當(dāng)前post事件的狀態(tài)蜡感。post()方法就是設(shè)置一些postState的屬性蹬蚁,然后遍歷事件消息隊(duì)列恃泪,調(diào)用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));
}
}
}
這個(gè)方法中同樣也用到了eventInheritance這個(gè)開關(guān)犀斋,即是否考慮Event事件類型的繼承關(guān)系贝乎,默認(rèn)為true,這里的lookupAllEventTypes()方法是EventBus的靜態(tài)方法叽粹,查找eventClass所有包括自己在內(nèi)的父類以及它們所實(shí)現(xiàn)的接口览效,然后對(duì)于沒有eventClass,調(diào)用postSingleForEventType()方法虫几,返回的結(jié)果為是否找到了對(duì)應(yīng)的訂閱方法锤灿,在沒有找到的情況下,會(huì)做出打印Log信息和發(fā)送事件處理辆脸,這里的logNoSubscriberMessages和sendNoSubscriberEvent是EventBus的開關(guān)屬性但校,與eventInheritance類似,也可以在Builder中設(shè)置啡氢,默認(rèn)為true状囱。如果當(dāng)我們調(diào)用post()方法發(fā)出某個(gè)事件時(shí)想知道我們的事件有沒有被訂閱者接收,就可以在發(fā)送消息的類中接收NoSubscriberEvent事件倘是,如果收到該事件說明應(yīng)用中沒有訂閱者接收我們發(fā)出的事件亭枷。
這里在看源碼時(shí)有一點(diǎn)點(diǎn)小疑問,就是在注冊(cè)時(shí)處理sticky事件時(shí)是找到Event的所有子類并發(fā)送給該訂閱者搀崭,而這里是Event的所有父類叨粘,并將其發(fā)送出去。前者是站在訂閱者的角度上门坷,訂閱者在注冊(cè)時(shí)要求接收某個(gè)sticky事件宣鄙,那么該事件的所有子類也是該sticky事件的一種,所以應(yīng)該發(fā)送給該訂閱者默蚌。比如一個(gè)訂閱者訂閱天氣預(yù)報(bào)的sticky事件冻晤,那么如果在stickyEvent中有一個(gè)今天下雨的事件(假設(shè)該事件繼承自天氣預(yù)報(bào),隨便想的例子绸吸,可能不太恰當(dāng))也應(yīng)該發(fā)送給該訂閱者鼻弧。而此處postSingleEvent方法中是發(fā)送單個(gè)Event,是站在發(fā)送者發(fā)送到EventBus的角度上锦茁,我需要發(fā)送某個(gè)事件通知訂閱者攘轩,比如我發(fā)送一個(gè)天氣預(yù)報(bào)的事件到EventBus, 但是EventBus由于不知道天氣預(yù)報(bào)是否表示下雨,就不應(yīng)該通知哪些是否下雨事件的訂閱者码俩,相反那些監(jiān)聽新聞的訂閱者(假設(shè)天氣預(yù)報(bào)繼承自新聞度帮,而天氣預(yù)報(bào)是一條新聞),EventBus需要負(fù)責(zé)通知他們這條天氣預(yù)報(bào)的新聞。被觀察者沒有將新聞發(fā)送到EventBus上笨篷,但是EventBus則因?yàn)橐粭l天氣預(yù)報(bào)需要找出新聞并發(fā)送到相應(yīng)的訂閱者上瞳秽,所以發(fā)送是兩個(gè)過程。接下來是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是為了線程安全练俐,每次對(duì)List的修改都會(huì)重新一份,由于是線程安全的所以不需要同步處理冕臭,但是對(duì)HashMap的讀取操作則不是線程安全的腺晾,所以需要線程同步。這個(gè)方法的邏輯也很簡(jiǎn)單辜贵,就是從subscriptionsByEventType中找出事件類型對(duì)應(yīng)的注冊(cè)信息列表悯蝉,然后遍歷調(diào)用postToSubscription()方法,這個(gè)方法有些熟悉了念颈,就是在注冊(cè)中處理sticky事件時(shí)調(diào)用的方法泉粉。不過這里需要注意的是abort每次都會(huì)讀取postingState的cancel狀態(tài)判斷發(fā)送事件是否被終止连霉,而另外兩個(gè)遍歷 event和subscription的賦值和清空也是為了用于cancelEventDelivery()方法榴芳,后面會(huì)統(tǒng)一說。最后終于到了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);
}
}
這里就到了真正的事件分發(fā)了窟感,分為我們所熟知的四種threadModed的情形,其代碼邏輯很清晰歉井,根據(jù)threadMode以及isMainThread選擇調(diào)用invokeSubscriber方法還是加入相應(yīng)的隊(duì)列柿祈,異步執(zhí)行。關(guān)于入隊(duì)異步執(zhí)行放在第三篇中講述哩至,重點(diǎn)分析三個(gè)Poster的實(shí)現(xiàn)躏嚎,這里先貼出invokeSubscriber的代碼,其實(shí)就是利用反射調(diào)用訂閱者的方法菩貌,方法存儲(chǔ)在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);
}
}
到這里事件的分發(fā)就結(jié)束了卢佣,梳理一下就是一下流程: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).
其中第一步是遍歷一個(gè)線程本地變量中保存的事件消息隊(duì)列的所有消息,第二步是遍歷一個(gè)事件類型的所有父類箭阶,第三步是遍歷一個(gè)事件類型的所有注冊(cè)信息虚茶,第四步則是事件分發(fā),根據(jù)threadMode選擇合適的處理方式股冗。
讀到這里就感覺優(yōu)秀代碼的確是邏輯結(jié)構(gòu)十分清晰杰妓,看起來一目了然秽澳,在一個(gè)任務(wù)中合理的劃分步驟,拆分成多個(gè)方法罩扇,讓人更容易理解。
接下來就是前面一直提到的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;
}
這里重點(diǎn)先看一下注釋怕磨,這個(gè)方法的作用就是在事件處理方法中調(diào)用喂饥,終止事件的進(jìn)一步傳遞寞缝,這個(gè)和android系統(tǒng)中的順序廣播是相同的道理(即通過提高自己的的優(yōu)先權(quán)可以率先收到事件,然后截取該事件)仰泻。但是這里有一個(gè)條件就是事件處理方法的threadMode必須是POSTING荆陆,即執(zhí)行線程與事件的發(fā)送線程為同一個(gè)線程。
從代碼中可以看出終止事件條件十分苛刻集侯,PostState中的三個(gè)變量都是用來判斷是否可以終止事件的被啼,第一個(gè)是isPosting,該變量在post方法中被設(shè)置true棠枉,表示一個(gè)事件類型及其父類正在被發(fā)送的狀態(tài)中浓体,發(fā)送完畢以后才被設(shè)置為false。所以在這個(gè)過程以外都不可以調(diào)用該方法辈讶。最后兩個(gè)條件postSingleEventForEventType中被設(shè)置的命浴,對(duì)于一個(gè)事件類型(不包括其父類)遍歷它的所有注冊(cè)信息,針對(duì)每一個(gè)注冊(cè)信息調(diào)用postToSubscription方法之前和之后這兩個(gè)變量都會(huì)被設(shè)置贱除,就是為了在這里判斷生闲,是否可以在某一個(gè)注冊(cè)信息的方法被調(diào)用時(shí)終止這個(gè)事件的繼續(xù)發(fā)送。(這里有一點(diǎn)不明白月幌,一個(gè)事件的所有注冊(cè)信息遍歷時(shí)碍讯,事件是同一個(gè),為什么在遍歷過程中都要賦值和清空扯躺,這里是否可以改成設(shè)置一次和清空一次即可捉兴?)。最后在設(shè)置了canceled變量以后(如果cancelEventDelivery在事件處理方法中被調(diào)用了)录语,事件處理方法返回之后倍啥,postToSubscription接著返回,canceled被賦值到了abort變量澎埠,這時(shí)候abort如果為true, 則break跳出循環(huán)虽缕,從而終止了該事件類型的繼續(xù)發(fā)送到其他的注冊(cè)信息。(注意這里不會(huì)影響其他事件類型失暂,如其父類事件等)
看到這里也就明白了為什么限制在threadMode為POSTING的事件處理方法中調(diào)用cancelEventDelivery方法了彼宠,這是因?yàn)閜ost()之后的一系列方法是在事件發(fā)送的線程中執(zhí)行,而這些狀態(tài)字的賦值與判斷必須處在同一線程中才能有效弟塞,所以事件處理方法必須與post()方法處在同一線程凭峡,所以也就只能是POSTING模式下才能保證。
最后invokeSubscriber方法中在調(diào)用訂閱者方法失敗時(shí)有一個(gè)異常處理方法决记,其代碼如下:
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);
}
}
}
代碼邏輯很簡(jiǎn)單摧冀,通常由于是發(fā)送的是我們自定義的Event, 所以會(huì)走else, 接著就會(huì)根據(jù)開關(guān)拋異常打log以及發(fā)送事件等,讀一下這段代碼也有利于我們以后調(diào)試有關(guān)EventBus的相關(guān)問題。
至此事件分發(fā)邏輯就分析結(jié)束了索昂。下一步簡(jiǎn)單介紹和sticky事件相關(guān)的幾個(gè)方法建车。
4. sticky事件
在注冊(cè)部分我們提到過sticky事件,即在訂閱時(shí)即可以接收到之前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);
}
之前可能對(duì)于sticky的解釋不太清楚缤至,不過這個(gè)方法的注釋則對(duì)sticky事件的解釋很清晰,淺顯易懂康谆。接著需要注意方法中的那句注釋领斥,就是需要先將sticky事件保存到stickyEvents中在調(diào)用post()方法,是為了防止remove失敗沃暗,如在事件處理方法中調(diào)用removeStickyEvent月洛,remove在put之前則會(huì)造成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();
}
}
這幾個(gè)方法都比較容易理解,不再解釋了惜辑,最后關(guān)于sticky還有一個(gè)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));
}
}
最后貼出來兩個(gè)靜態(tài)方法,就是前面提到的查找事件類型和它所有父類以及它們的實(shí)現(xiàn)的所有接口的方法韵丑,對(duì)于過程的分析無關(guān)緊要爵卒,有興趣的可以看一下源碼虚缎,如下:
/** 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這個(gè)類的代碼幾乎全部分析結(jié)束了,還有幾個(gè)方法會(huì)在其他地方用到实牡,不會(huì)影響這個(gè)注冊(cè)和分發(fā)的流程陌僵,所以這里不再貼出其源碼了,有興趣的可以自行查看创坞。
后記
本篇文章主要分析了EventBus這個(gè)類碗短,分析了EventBus基于建造者模式的構(gòu)造器以及整個(gè)流程中的注冊(cè)訂閱者和事件分發(fā)的流程,重點(diǎn)是EventBus中設(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu)题涨,以及在過程中對(duì)這些數(shù)據(jù)結(jié)構(gòu)的操作偎谁,從中有很多的借鑒意義。在這整個(gè)流程中還有兩個(gè)重要的步驟纲堵,一是查找所有訂閱方法的信息巡雨,使用反射查找方法信息較為簡(jiǎn)單,由于在EventBus3.0之后使用了注解席函,訂閱方法的名字不再限制為onEvent()铐望,編程更為方便,但是需要對(duì)訂閱方法的查詢,反射由于處于運(yùn)行期影響性能正蛙,所以EventBus選擇在編譯期處理注解督弓,查詢訂閱方法信息,這部分在下一篇文章中做分析乒验。第二個(gè)沒有分析的重要的步驟就是三個(gè)Poster的實(shí)現(xiàn)愚隧,即如何在不同線程中發(fā)送事件,我們都知道android的異步事件都是通過handler處理锻全,EventBus也是基于Handler進(jìn)行異步處理奸攻,但是EventBus使用明顯方便太多,這一切都?xì)w功于Poster虱痕,對(duì)于Poster的分析則放在第三篇文章中分析睹耐。