十、EventBus 原始碼隨想
EventBus 原始碼隨想
首先網上已經有不少優秀的EventBus的原始碼分析文章,這篇只是為了記錄自己的理解,畢竟自己親自寫出來才能理解的更深,所以如有不對的地方,還望諒解。
參考
ofollow,noindex">https://www.jianshu.com/p/f057c460c77e
http://p.codekk.com/blogs/detail/54cfab086c4761e5001b2538
https://kymjs.com/code/2015/12/16/01/0. 幾個問題
EventBus 的使用過程無非就是 註冊、post、響應事件函式。
那麼我們得弄清楚這幾個問題
1、怎樣進行註冊的 ?
2、post 的事件是 什麼,即 post 裡的引數到底代表著什麼?
3、post 時,訂閱者是怎樣收到響應的,是怎麼通知到所有與事件相關的訂閱者的?
1. 註冊
// 把當前類註冊為訂閱者(Subscriber) EventBus.getDefault().register(this); // 解除當前類的註冊 EventBus.getDefault().unregister(this);
程式碼很簡單,就一行,那麼我們來看看到底是怎樣註冊的。在註冊的時候究竟做了什麼。在整個 EventBus 的使用過程中,除了註冊大部分就是 post 和 事件響應函數了,所以我們猜測在註冊的時候,應該會把事件和訂閱者繫結起來。那麼我們帶著這個疑惑去看這段程式碼。
1.1 獲取 EventBus 物件
首先 Event.getDefault() 看到這個應該能想到是個 單例模式。
static volatile EventBus defaultInstance; public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
果不其然,一個雙重校驗鎖的單例模式。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder(); public EventBus() { this(DEFAULT_BUILDER); } EventBus(EventBusBuilder builder) { // 省略 }
可以看到上面建構函式傳入了一個 builder,很明顯 建造者模式 。 至此,我們可以獲取到了一個單例的 EventBus 的物件了,另外關於 builder 的更詳細的內容可自己看原始碼。
1.2 register
獲取到物件後,就呼叫 register 方法了,裡面傳入了 this 引數。
public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
我們可以看到 register 方法的引數名是 subscriber,說明我們傳進去的 this 就是訂閱者物件。此時我們清楚了 當前類就是訂閱者。
這個方法裡面做了什麼呢,我們來看一看。首先獲取到傳進去的 this (即當前類)的 Class 物件,然後呼叫 subscriberMethodFinder.findSubscriberMethods(subscriberClass); 我們根據名字可以猜想,這應該是去找到當前訂閱者的所有 事件響應函式(即帶有 @Subscribe 註解的方法) 。 找到所有事件響應函式後,就呼叫 subscribe, 那這裡我們就可以猜想是把 當前類物件 與這些 事件響應函式 關聯起來。下面驗證我們的猜想。
1.2.1 findSubscriberMethods
首先我們得找到當前類的所有 合法的事件響應函式
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } 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; } }
這裡面我們直接找到 findUsingReflection 方法, 根據方法名也知道這是什麼意思了。 繼續跟進
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findUsingReflectionInSingleClass(findState); findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
這裡的 FindState 用於做 事件響應函式 的校驗和儲存。繼續跟進 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(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { Class<?> eventType = parameterTypes[0]; if (findState.checkAdd(method, eventType)) { ThreadMode threadMode = subscribeAnnotation.threadMode(); 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"); } } }
這裡如果知道反射的應該都看得懂,沒什麼複雜的地方。 從這裡的實現我們可以知道,這個 eventType 其實是第一個引數型別, 並且保證只能有一個引數。 這樣我們的事件其實是以 事件響應函式的 引數型別 為基準的,可以看到如果引數的數量不為 1 ,會丟擲異常。
最後再通過 findUsingReflection 方法的 getMethodsAndRelease 返回一個 List<SubscriberMethod>
至此, register 方法的 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
這一行程式碼就走完了,我們拿到了該訂閱類的所有 事件響應函式 , 即 SubscriberMethod 的集合。
1.2.2 subscribe
接著繼續 register 往下走,可以看到會迴圈 List<SubscriberMethod>
這個集合,取出每個 SubscriberMethod(事件響應函式),然後呼叫 subscribe,我們看看這個方法到底做了什麼。
這裡先給出結果,這個方法就是我們註冊的關鍵了,它完成了我們每個 事件響應函式的註冊過程。
首先這個方法會涉及兩個變數,我一開始特別懵,完全不知道這兩個變數啥意思,不過圖一畫出來就立馬清晰了。
這兩個變數是 subscriptionsByEventType 和 typesBySubscriber 。基本上理解這兩個變數是什麼意思,整個註冊流程就通了。
首先我們定義兩個類和方法,如下所示,這個得認真理清楚。
public class A { @Subscribe public void testA1(Event1 event1) { //do something } @Subscribe public void testA2(Event2 event2) { //do something } } public class B { @Subscribe public void testB1(Event1 event1) { //do something } @Subscribe public void testB2(Event2 event2) { //do something } }
可以看到這兩個類分別有著兩個事件響應函式,事件型別有兩種,Event1 和 Event2
subscriptionsByEventType這個變數是 Map<Class<?>, CopyOnWriteArrayList<Subscription>>
型別,它的圖示如下:

subscriptionsByEventType.png
typesBySubscriber這個變數是 Map<Object, List<Class<?>>>
類都,它的圖示如下:

typesBySubscriber.png
至於其中的 SubscriberMethod 物件,在 findUsingReflectionInSingleClass 方法中可以看到是怎樣構造出來的。這個物件也是非常重要的,裡面封裝了 方法資訊 method、執行緒模式 threadMode、引數型別 eventType、優先順序 priority、sticky 布林值 等資訊。
相信這兩個圖可以幫助你很好的理解接下來的 subscribe 流程。關於 subscribe 的原始碼,以及流程我就不分析了,自己看原始碼對著上面兩個圖絕對能理解。 不能理解的話,還有 參考 內的文章,這裡一定得自己去弄懂。當然並不複雜,所以耐心點。
當 subscribe 走完後,我們的 EventBus 擁有了什麼?
它擁有了 事件型別(例 Event1) 所對應的 Subscription 資訊列表,什麼意思, 就是這個 事件 在 哪些類的哪些方法存在 ,有了這些資訊,EventBus 就能很方便的將一個 事件 傳遞給所有 訂閱了這個事件的 方法了。
那麼 typesBySubscriber 有什麼用呢,在 unregister 時,它可以發揮它的作用。那麼趁熱打鐵,看看 unregister 是如何工作的。噢,對了,放上註冊的流程圖,是取自 codekk 的流程圖,相信再看這個圖,你會非常清晰。

register-flow-chart.png
2 unregister 解除註冊
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 { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }
第一行就用到了剛剛的 typesBySubscriber, 這裡將當前訂閱者的 事件型別列表 取出來,也就是拿到了當前類的所有 事件。
然後判斷 事件列表 是否為空,不為空則 迴圈事件列表,依次呼叫 unsubscribeByEventType(subscriber, eventType);
最後呼叫 typesBySubscriber.remove(subscriber);
這個是把 當前訂閱者 從 typesBySubscriber 中刪掉,這樣就完成了解除繫結的操作。當然最重要的是 unsubscribeByEventType 這個方法。
2.1 unsubscribeByEventType
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--; } } } }
這個根據上面的 subscriptionsByEventType 圖示,應該不難理解。
首先從 subscriptionsByEventType 中拿到當前 事件(eventType)的 Subscription 列表。 然後迴圈這個列表,從中找到傳進來的 訂閱者(subscriber 即 當前解除註冊的那個 類,找到後,從 Subscription 列表 中刪除即可。
3. post 釋出事件
前面註冊和解除註冊兩個流程都走通了,那麼只剩下 post 了。關於 post 的流程, 參考 內的文章都講的很好,礙於篇幅,就不重複闡述了,這裡說說我剛看時,不懂的幾個地方。
1、 postSingleEvent 方法會先去得到該事件型別的所有父類及介面型別,然後迴圈 呼叫 postSingleEventForEventType 函式釋出每個事件到每個訂閱者? 這裡說了這麼多是什麼意思。
2、 ThreadMode 對應的四種狀態的 Poster。
針對第一個問題很好解釋,我們基於上面的 Event1 事件解釋,假設有一個 訂閱者訂閱了 Object 型別的事件,而 Event1 的父類是 Object,那麼我們 post Event1事件 時, 訂閱了 Event1 事件的訂閱者 和 訂閱了 Object 事件的訂閱者 是不是都得接收到這個事件。所以我們需要處理父類及其介面型別的 post。
第二個問題,看了 post 的原始碼應該知道最後會進入到 postToSubscription 這個方法,判斷 subscription.subscriberMethod.threadMode 然後呼叫 invokeSubscriber 完成 post流程。
那麼這個 threadMode 有四種狀態,
3.1 POSTING
首先看下這個 POSTING 狀態,這是 預設的 ThreadMode,表示在執行 Post 操作的執行緒直接呼叫訂閱者的事件響應方法,不論該執行緒是否為主執行緒(UI 執行緒),也就是說你是哪個執行緒就會在哪個執行緒執行。 程式碼中可以看到直接呼叫了 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); } }
就是通過反射執行了 method , 這樣我們也就清楚了,事件函式得到了響應。
注意: 若 Post 執行緒為主執行緒,別忘了不能進行耗時操作
3.2 MAIN
case MAIN: if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break;
這個狀態我們可以看到,首先會判斷是否是 主執行緒, 如果是直接呼叫 invokeSubscriber ,如果不是會去呼叫 mainThreadPoster.enqueue 。 那麼這個 mainThreadPoster 是什麼?
3.2.1 mainThreadPoster
這個 mainThreadPoster 會在建構函式中進行初始化,自己可以去原始碼裡看看, 可以知道最後就是 new 一個 HandlerPoster。
看看 HandlerPoster 的建構函式
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) { super(looper); this.eventBus = eventBus; this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; queue = new PendingPostQueue(); }
這個 looper 是主執行緒的 looper,也就是說最後傳送的訊息,會在 主執行緒去處理 。
然後會 new 一個 PendingPostQueue, 這個 PendingPostQueue 是一個 PendingPost 型別的佇列。
而 PendingPost 則是 訂閱者和事件資訊實體類,並含有同一佇列中指向下一個物件的指標。通過快取儲存不用的物件,減少下次建立的效能消耗。
會看的很懵,其實很好理解, PendingPostQueue就相當於 MessageQueue, PendingPost 則相當於 Message。當然只是類比,在呼叫 enqueue 的時候,會發送一個空的 Message,如下程式碼:
public void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); synchronized (this) { queue.enqueue(pendingPost); if (!handlerActive) { handlerActive = true; if (!sendMessage(obtainMessage())) { throw new EventBusException("Could not send handler message"); } } } }
可以看到我們把 pendingPost 放入到 PendingPostQueue, 然後傳送一個空的 Message, 我們理解 Handler 機制,所以去看 handleMessage 吧。
@Override public void handleMessage(Message msg) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { PendingPost pendingPost = queue.poll(); if (pendingPost == null) { synchronized (this) { // Check again, this time in synchronized pendingPost = queue.poll(); if (pendingPost == null) { handlerActive = false; return; } } } eventBus.invokeSubscriber(pendingPost); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage())) { throw new EventBusException("Could not send handler message"); } rescheduled = true; return; } } } finally { handlerActive = rescheduled; } }
程式碼很長,但是你可以很清楚的看到這句程式碼 eventBus.invokeSubscriber(pendingPost);
還記得 invokeSubscriber 方法嗎,沒錯就是 通過反射執行 method。 當然這裡過載了該 方法, 但是最終還是會走到 那個有兩個引數的 invokeSubscriber 方法。
到這裡,我們的 MAIN 狀態的 post 流程也走完了。
3.3 BACKGROUND 和 ASYNC
這兩個狀態希望大家自己去看原始碼了,並沒有什麼複雜的,無非就是 backgroundPoster 和 asyncPoster 對執行緒處理的不同,這兩個 Poster 內部同樣有 PendingPostQueue 。
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
這是 AsyncPoster 和 BackgroundPoster 內部的執行緒池 在 EventBusBuilder 中的定義。