衆所周知,EventBus 是一款用在 Android 開發中的發佈/訂閱事件總線框架,基於觀察者模式,將事件的接收者和發送者分開,簡化了組件之間的通訊操做,使用簡單、效率高、體積小!java
EventBus使用了典型的發佈/訂閱事件模式,下面是 EventBus官方給出的原理示意圖。git
使用EventBus以前,須要先添加EventBus依賴,EventBus支持gradle和maven兩種方式依賴,gradle依賴的腳本以下:github
implementation 'org.greenrobot:eventbus:3.1.1'
maven依賴的配置以下:緩存
<dependency> <groupId>org.greenrobot</groupId> <artifactId>eventbus</artifactId> <version>3.1.1</version> </dependency>
EventBus的使用步驟分爲定義事件、訂閱事件、發送事件、處理事件、取消訂閱五步。
1,首先,定義一個事件類,裏面添加須要發送的數據內容,以下:框架
public static class MessageEvent { /* Additional fields if needed */ }
2,而後,在須要接收事件的地方訂閱事件,能夠選擇註冊事件訂閱方法。async
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { /* Do something */ };
爲了避免形成資源的浪費或其餘問題,須要在onStart函數中註冊訂閱事件,而後再onStop函數中取消訂閱事件。maven
@Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); }
3,最後,將處理完成的數據發送出去。ide
EventBus.getDefault().post(new MessageEvent());
要理解EventBus背後的原理,能夠從如下幾個方面着手:函數
EventBus從3.0開始使用Subscribe註解配置事件訂閱方法,再也不使用方法名,例如:post
@Subscribe public void handleEvent(String event) { // do something }
其中,事件類型能夠是 Java 中已有的類型或者自定義的類型。下面是Subscribe註解的具體實現:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { // 指定事件訂閱方法的線程模式,即在那個線程執行事件訂閱方法處理事件,默認爲POSTING ThreadMode threadMode() default ThreadMode.POSTING; // 是否支持粘性事件,默認爲false boolean sticky() default false; // 指定事件訂閱方法的優先級,默認爲0,若是多個事件訂閱方法能夠接收相同事件的,則優先級高的先接收到事件 int priority() default 0; }
能夠發現,在使用Subscribe註解時能夠根據需求指定threadMode、sticky、priority三個屬性。其中,threadMode屬性有以下幾個可選值:
使用EventBus時,須要在在須要接收事件的地方訂閱事件,註冊事件的方式以下:
EventBus.getDefault().register(this);
點擊打開getDefault()會發現,getDefault()是一個單例方法,保證當前只有一個EventBus實例。
public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
getDefault()最終調用了new EventBus(),以下:
public EventBus() { this(DEFAULT_BUILDER); }
而後,EventBus調用它的另外一個構造函數來完成它相關屬性的初始化:
EventBus(EventBusBuilder builder) { logger = builder.getLogger(); subscriptionsByEventType = new HashMap<>(); typesBySubscriber = new HashMap<>(); stickyEvents = new ConcurrentHashMap<>(); mainThreadSupport = builder.getMainThreadSupport(); mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; 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; }
其中,DEFAULT_BUILDER就是一個默認的EventBusBuilder,以下:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
若是有須要的話,咱們也能夠經過配置EventBusBuilder來更改EventBus的屬性,例如:
EventBus.builder() .eventInheritance(false) .logSubscriberExceptions(false) .build() .register(this);
有了EventBus的實例,接下來就能夠進行註冊了。
public void register(Object subscriber) { // 獲得當前要註冊類的Class對象 Class<?> subscriberClass = subscriber.getClass(); // 根據Class查找當前類中訂閱了事件的方法集合,即便用了Subscribe註解、有public修飾符、一個參數的方法 // SubscriberMethod類主要封裝了符合條件方法的相關信息: // Method對象、線程模式、事件類型、優先級、是不是粘性事等 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { // 循環遍歷訂閱了事件的方法集合,以完成註冊 for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
能夠看到,register()方法主要分爲查找和註冊兩部分,首先來看查找的過程,主要是findSubscriberMethods()方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { // METHOD_CACHE是一個ConcurrentHashMap,直接保存了subscriberClass和對應SubscriberMethod的集合,以提升註冊效率,賦值重複查找。 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } // 因爲使用了默認的EventBusBuilder,則ignoreGeneratedIndex屬性默認爲false,便是否忽略註解生成器 if (ignoreGeneratedIndex) { subscriberMethods = findUsingReflection(subscriberClass); } else { subscriberMethods = findUsingInfo(subscriberClass); } // 若是對應類中沒有符合條件的方法,則拋出異常 if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation"); } else { // 保存查找到的訂閱事件的方法 METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; } }
findSubscriberMethods()流程很清晰,即先從緩存中查找,若是找到則直接返回,不然去作下一步的查找過程,而後緩存查找到的集合,根據上邊的註釋可知findUsingInfo()方法會被調用。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); // 初始狀態下findState.clazz就是subscriberClass while (findState.clazz != null) { findState.subscriberInfo = getSubscriberInfo(findState); // 條件不成立 if (findState.subscriberInfo != null) { SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); for (SubscriberMethod subscriberMethod : array) { if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { findState.subscriberMethods.add(subscriberMethod); } } } else { // 經過反射查找訂閱事件的方法 findUsingReflectionInSingleClass(findState); } // 修改findState.clazz爲subscriberClass的父類Class,即須要遍歷父類 findState.moveToSuperclass(); } // 查找到的方法保存在了FindState實例的subscriberMethods集合中。 // 使用subscriberMethods構建一個新的List<SubscriberMethod> // 釋放掉findState return getMethodsAndRelease(findState); }
findUsingInfo()方法會在當前要註冊的類以及其父類中查找訂閱事件的方法,這裏出現了一個FindState類,它是SubscriberMethodFinder的內部類,用來輔助查找訂閱事件的方法,具體的查找過程在findUsingReflectionInSingleClass()方法裏,它主要經過反射來查找訂閱事件的方法。
private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 methods = findState.clazz.getMethods(); findState.skipSuperClasses = true; } // 循環遍歷當前類的方法,篩選出符合條件的 for (Method method : methods) { // 得到方法的修飾符 int modifiers = method.getModifiers(); // 若是是public類型,但非abstract、static等 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { // 得到當前方法全部參數的類型 Class<?>[] parameterTypes = method.getParameterTypes(); // 若是當前方法只有一個參數 if (parameterTypes.length == 1) { Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); // 若是當前方法使用了Subscribe註解 if (subscribeAnnotation != null) { // 獲得該參數的類型 Class<?> eventType = parameterTypes[0]; // checkAdd()方法用來判斷FindState的anyMethodByEventType map是否已經添加過以當前eventType爲key的鍵值對,沒添加過則返回true if (findState.checkAdd(method, eventType)) { // 獲得Subscribe註解的threadMode屬性值,即線程模式 ThreadMode threadMode = subscribeAnnotation.threadMode(); // 建立一個SubscriberMethod對象,並添加到subscriberMethods集合 findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException("@Subscribe method " + methodName + "must have exactly 1 parameter but has " + parameterTypes.length); } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); } } }
到此register()方法中findSubscriberMethods()流程就分析完了,咱們已經找到了當前註冊類及其父類中訂閱事件的方法的集合。
接下來,咱們分析下具體的註冊流程,即register()中的subscribe()方法。首先,咱們看一下subscribe()方法的源碼:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { // 獲得當前訂閱了事件的方法的參數類型 Class<?> eventType = subscriberMethod.eventType; // Subscription類保存了要註冊的類對象以及當前的subscriberMethod Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // subscriptionsByEventType是一個HashMap,保存了以eventType爲key,Subscription對象集合爲value的鍵值對 // 先查找subscriptionsByEventType是否存在以當前eventType爲key的值 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); // 若是不存在,則建立一個subscriptions,並保存到subscriptionsByEventType 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); } } // 添加上邊建立的newSubscription對象到subscriptions中 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; } } // typesBySubscribere也是一個HashMap,保存了以當前要註冊類的對象爲key,註冊類中訂閱事件的方法的參數類型的集合爲value的鍵值對 // 查找是否存在對應的參數類型集合 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); // 不存在則建立一個subscribedEvents,並保存到typesBySubscriber 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); } } }
能夠發現,subscribe()方法主要是獲得了subscriptionsByEventType、typesBySubscriber兩個 HashMap。其中,發送事件的時候要用到subscriptionsByEventType,完成事件的處理。當取消 EventBus 註冊的時候要用到typesBySubscriber、subscriptionsByEventType,完成相關資源的釋放。
接下來,咱們看一下EventBus 取消事件註冊的流程。
EventBus.getDefault().unregister(this);
其中,unregister()的源碼以下:
public synchronized void unregister(Object subscriber) { // 獲得當前註冊類對象 對應的 訂閱事件方法的參數類型 的集合 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { // 遍歷參數類型集合,釋放以前緩存的當前類中的Subscription for (Class<?> eventType : subscribedTypes) { unsubscribeByEventType(subscriber, eventType); } // 刪除以subscriber爲key的鍵值對 typesBySubscriber.remove(subscriber); } else { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }
unregister()方法調用了unsubscribeByEventType()方法:
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { // 獲得當前參數類型對應的Subscription集合 List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions != null) { int size = subscriptions.size(); // 遍歷Subscription集合 for (int i = 0; i < size; i++) { Subscription subscription = subscriptions.get(i); // 若是當前subscription對象對應的註冊類對象 和 要取消註冊的註冊類對象相同,則刪除當前subscription對象 if (subscription.subscriber == subscriber) { subscription.active = false; subscriptions.remove(i); i--; size--; } } } }
因此,在unregister()方法中,最主要的就是釋放typesBySubscriber、subscriptionsByEventType中緩存的資源。
在EventBus中,咱們發送一個事件使用的是以下的方式:
EventBus.getDefault().post("Hello World!")
能夠看到,發送事件就是經過post()方法完成的,post()方法的源碼以下:
public void post(Object event) { // currentPostingThreadState是一個PostingThreadState類型的ThreadLocal // PostingThreadState類保存了事件隊列和線程模式等信息 PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; // 將要發送的事件添加到事件隊列 eventQueue.add(event); // isPosting默認爲false if (!postingState.isPosting) { // 是否爲主線程 postingState.isMainThread = isMainThread(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } try { // 遍歷事件隊列 while (!eventQueue.isEmpty()) { // 發送單個事件 // eventQueue.remove(0),從事件隊列移除事件 postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } }
能夠發現,post()方法先將發送的事件保存到List的事件隊列,而後經過循環出隊列,將事件交給postSingleEvent()方法處理。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; // eventInheritance默認爲true,表示是否向上查找事件的父類 if (eventInheritance) { // 查找當前事件類型的Class,連同當前事件類型的Class保存到集合 List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); // 遍歷Class集合,繼續處理事件 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) { logger.log(Level.FINE, "No subscribers registered for event " + eventClass); } if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) { post(new NoSubscriberEvent(this, event)); } } }
postSingleEvent()方法中,根據eventInheritance屬性,決定是否向上遍歷事件的父類型,而後用postSingleEventForEventType()方法進一步處理事件。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { // 獲取事件類型對應的Subscription集合 subscriptions = subscriptionsByEventType.get(eventClass); } // 若是已訂閱了對應類型的事件 if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { // 記錄事件 postingState.event = event; // 記錄對應的subscription 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; }
能夠發現,postSingleEventForEventType()方法核心就是遍歷發送的事件類型對應的Subscription集合,而後調用postToSubscription()方法處理事件。
接着上面的postToSubscription()方法,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 { // 若是是在子線程發送事件,則將事件入隊列,經過Handler切換到主線程執行處理事件 // mainThreadPoster 不爲空 mainThreadPoster.enqueue(subscription, event); } break; // 不管在那個線程發送事件,都先將事件入隊列,而後經過 Handler 切換到主線程,依次處理事件。 // mainThreadPoster 不爲空 case MAIN_ORDERED: if (mainThreadPoster != null) { mainThreadPoster.enqueue(subscription, event); } else { invokeSubscriber(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); } }
能夠看到,postToSubscription()方法就是根據訂閱事件方法的線程模式、以及發送事件的線程來判斷如何處理事件,至於處理方式主要有兩種:一種是在相應線程直接經過invokeSubscriber()方法,用反射來執行訂閱事件的方法,這樣發送出去的事件就被訂閱者接收並作相應處理了。
首先,咱們來看一下invokeSubscriber()方法:
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); } }
若是在子線程發送事件,則直接在發送事件的線程經過反射處理事件。
另一種是先將事件入隊列(其實底層是一個List),而後作進一步處理,咱們以mainThreadPoster.enqueue(subscription, event)爲例簡單的分析下,其中mainThreadPoster是HandlerPoster類的一個實例。
public class HandlerPoster extends Handler implements Poster { private final PendingPostQueue queue; private boolean handlerActive; ...... public void enqueue(Subscription subscription, Object event) { // 用subscription和event封裝一個PendingPost對象 PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); synchronized (this) { // 入隊列 queue.enqueue(pendingPost); if (!handlerActive) { handlerActive = true; // 發送開始處理事件的消息,handleMessage()方法將被執行,完成從子線程到主線程的切換 if (!sendMessage(obtainMessage())) { throw new EventBusException("Could not send handler message"); } } } } @Override public void handleMessage(Message msg) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); // 死循環遍歷隊列 while (true) { // 出隊列 PendingPost pendingPost = queue.poll(); ...... // 進一步處理pendingPost eventBus.invokeSubscriber(pendingPost); ...... } } finally { handlerActive = rescheduled; } } }
能夠發現,HandlerPoster的enqueue()方法主要就是將subscription、event對象封裝成一個PendingPost對象,而後保存到隊列裏,以後經過Handler切換到主線程,在handleMessage()方法將中將PendingPost對象循環出隊列,交給invokeSubscriber()方法作進一步處理。
void invokeSubscriber(PendingPost pendingPost) { Object event = pendingPost.event; Subscription subscription = pendingPost.subscription; // 釋放pendingPost引用的資源 PendingPost.releasePendingPost(pendingPost); if (subscription.active) { // 用反射來執行訂閱事件的方法 invokeSubscriber(subscription, event); } }
invokeSubscriber方法比較簡單,主要就是從pendingPost中取出以前保存的event、subscription,而後用反射來執行訂閱事件的方法,又回到了第一種處理方式。因此mainThreadPoster.enqueue(subscription, event)的核心就是先將將事件入隊列,而後經過Handler從子線程切換到主線程中去處理事件。
backgroundPoster.enqueue()和asyncPoster.enqueue也相似,內部都是先將事件入隊列,而後再出隊列,可是會經過線程池去進一步處理事件。
通常狀況,咱們使用 EventBus 都是準備好訂閱事件的方法,而後註冊事件,最後在發送事件,即要先有事件的接收者。但粘性事件卻偏偏相反,咱們能夠先發送事件,後續再準備訂閱事件的方法、註冊事件。
首先,咱們看一下EventBus的粘性事件是如何使用的:
EventBus.getDefault().postSticky("Hello World!");
粘性事件使用了postSticky()方法,postSticky()方法的源碼以下:
public void postSticky(Object event) { synchronized (stickyEvents) { stickyEvents.put(event.getClass(), event); } post(event); }
postSticky()方法主要作了兩件事:先將事件類型和對應事件保存到stickyEvents中,方便後續使用;而後執行post(event)繼續發送事件,這個post()方法就是以前發送的post()方法。因此,若是在發送粘性事件前,已經有了對應類型事件的訂閱者,及時它是非粘性的,依然能夠接收到發送出的粘性事件。
發送完粘性事件後,再準備訂閱粘性事件的方法,並完成註冊。核心的註冊事件流程仍是咱們以前的register()方法中的subscribe()方法,前邊分析subscribe()方法時,有一段沒有分析的代碼,就是用來處理粘性事件的。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { ...... ...... ...... // 若是當前訂閱事件的方法的Subscribe註解的sticky屬性爲true,即該方法可接受粘性事件 if (subscriberMethod.sticky) { // 默認爲true,表示是否向上查找事件的父類 if (eventInheritance) { // stickyEvents就是發送粘性事件時,保存了事件類型和對應事件 Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, Object> entry : entries) { Class<?> candidateEventType = entry.getKey(); // 若是candidateEventType是eventType的子類或 if (eventType.isAssignableFrom(candidateEventType)) { // 得到對應的事件 Object stickyEvent = entry.getValue(); // 處理粘性事件 checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { Object stickyEvent = stickyEvents.get(eventType); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } }
能夠看到,處理粘性事件就是在 EventBus 註冊時,遍歷stickyEvents,若是當前要註冊的事件訂閱方法是粘性的,而且該方法接收的事件類型和stickyEvents中某個事件類型相同或者是其父類,則取出stickyEvents中對應事件類型的具體事件,作進一步處理。
subscribe()方法最核心的就是checkPostStickyEventToSubscription()方法。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { if (stickyEvent != null) { postToSubscription(newSubscription, stickyEvent, isMainThread()); } }
回顧上面對 EventBus 註冊事件流程的分析,EventBus主要是在項目運行時經過反射來查找訂事件的方法信息,若是項目中有大量的訂閱事件的方法,必然會對項目運行時的性能產生影響。其實除了在項目運行時經過反射查找訂閱事件的方法信息,EventBus 還提供了在項目編譯時經過註解處理器查找訂閱事件方法信息的方式,生成一個輔助的索引類來保存這些信息,這個索引類就是Subscriber Index,和 ButterKnife 的原理是相似的。
因此,咱們在添加EventBus依賴的時候一般是下面這樣的:
dependencies { compile 'org.greenrobot:eventbus:3.1.1' // 引入註解處理器 annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1' }
而後在項目的 Application 中添加以下配置,能夠生成一個默認的 EventBus 單例。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
其中,MyEventBusIndex()方法的源碼以下:
public class MyEventBusIndex implements SubscriberInfoIndex { private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; static { SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>(); putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("changeText", String.class), })); } private static void putIndex(SubscriberInfo info) { SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info); } @Override public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) { SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass); if (info != null) { return info; } else { return null; } } }
其中SUBSCRIBER_INDEX是一個HashMap,保存了當前註冊類的 Class 類型和其中事件訂閱方法的信息。
接下來,咱們再來分析下使用 Subscriber 索引時 EventBus 的註冊流程。首先,建立一個EventBusBuilder,而後經過addIndex()方法添加索引類的實例。
public EventBusBuilder addIndex(SubscriberInfoIndex index) { if (subscriberInfoIndexes == null) { subscriberInfoIndexes = new ArrayList<>(); } subscriberInfoIndexes.add(index); return this; }
即把生成的索引類的實例保存在subscriberInfoIndexes集合中,而後用installDefaultEventBus()建立默認的 EventBus實例。
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; } }
即用當前EventBusBuilder對象建立一個 EventBus 實例,這樣咱們經過EventBusBuilder配置的 Subscriber Index 也就傳遞到了EventBus實例中,而後賦值給EventBus的 defaultInstance成員變量。
因此在 Application 中生成了 EventBus 的默認單例,這樣就保證了在項目其它地方執行EventBus.getDefault()就能獲得惟一的 EventBus 實例!
因爲咱們如今使用了 Subscriber Index 因此不會經過findUsingReflectionInSingleClass()來反射解析訂閱事件的方法。咱們重點來看getSubscriberInfo()方法。
private SubscriberInfo getSubscriberInfo(FindState findState) { // 該條件不成立 if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo(); if (findState.clazz == superclassInfo.getSubscriberClass()) { return superclassInfo; } } // 該條件成立 if (subscriberInfoIndexes != null) { // 遍歷索引類實例集合 for (SubscriberInfoIndex index : subscriberInfoIndexes) { // 根據註冊類的 Class 類查找SubscriberInfo SubscriberInfo info = index.getSubscriberInfo(findState.clazz); if (info != null) { return info; } } } return null; }
subscriberInfoIndexes就是在前邊addIndex()方法中建立的,保存了項目中的索引類實例,即MyEventBusIndex的實例,繼續看索引類的getSubscriberInfo()方法,來到了MyEventBusIndex類中。
@Override public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) { SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass); if (info != null) { return info; } else { return null; } }
即根據註冊類的 Class 類型從 SUBSCRIBER_INDEX 查找對應的SubscriberInfo,若是咱們在註冊類中定義了訂閱事件的方法,則 info不爲空,進而上邊findUsingInfo()方法中findState.subscriberInfo != null成立,到這裏主要的內容就分析完了,其它的和以前的註冊流程同樣。
因此 Subscriber Index 的核心就是項目編譯時使用註解處理器生成保存事件訂閱方法信息的索引類,而後項目運行時將索引類實例設置到 EventBus 中,這樣當註冊 EventBus 時,從索引類取出當前註冊類對應的事件訂閱方法信息,以完成最終的註冊,避免了運行時反射處理的過程,因此在性能上會有質的提升。項目中能夠根據實際的需求決定是否使用 Subscriber Index。
下面咱們再來看一下EventBus的完成流程,能夠用如下的幾張圖: