深扒 EventBus:register
本文章已授權玉剛說微信公眾號預釋出中
上一篇: 深扒 EventBus:getDefault
FindState

再看 EventBus.register 方法原始碼之前,我們先來了解 SubscriberMethodFinder 的一個靜態內部類 FindState,從字面上理解是查詢狀態,那到底是什麼東西的查詢狀態呢,讓我們先簡單過一遍原始碼
static class FindState { final List<SubscriberMethod> subscriberMethods = new ArrayList<>(); final Map<Class, Object> anyMethodByEventType = new HashMap<>(); final Map<String, Class> subscriberClassByMethodKey = new HashMap<>(); final StringBuilder methodKeyBuilder = new StringBuilder(128); Class<?> subscriberClass; Class<?> clazz; boolean skipSuperClasses; SubscriberInfo subscriberInfo; void initForSubscriber(Class<?> subscriberClass) { this.subscriberClass = clazz = subscriberClass; skipSuperClasses = false; subscriberInfo = null; } void recycle() { subscriberMethods.clear(); anyMethodByEventType.clear(); subscriberClassByMethodKey.clear(); methodKeyBuilder.setLength(0); subscriberClass = null; clazz = null; skipSuperClasses = false; subscriberInfo = null; } boolean checkAdd(Method method, Class<?> eventType) { // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required. // Usually a subscriber doesn't have methods listening to the same event type. Object existing = anyMethodByEventType.put(eventType, method); if (existing == null) { return true; } else { if (existing instanceof Method) { if (!checkAddWithMethodSignature((Method) existing, eventType)) { // Paranoia check throw new IllegalStateException(); } // Put any non-Method object to "consume" the existing Method anyMethodByEventType.put(eventType, this); } return checkAddWithMethodSignature(method, eventType); } } private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) { methodKeyBuilder.setLength(0); methodKeyBuilder.append(method.getName()); methodKeyBuilder.append('>').append(eventType.getName()); String methodKey = methodKeyBuilder.toString(); Class<?> methodClass = method.getDeclaringClass(); Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass); if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) { // Only add if not already found in a sub class return true; } else { // Revert the put, old class is further down the class hierarchy subscriberClassByMethodKey.put(methodKey, methodClassOld); return false; } } void moveToSuperclass() { if (skipSuperClasses) { clazz = null; } else { clazz = clazz.getSuperclass(); String clazzName = clazz.getName(); /** Skip system classes, this just degrades performance. */ if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) { clazz = null; } } } }
什麼?看完很懵逼?這裡我們可以簡化一下
static class FindState { // 訂閱方法的集合 final List<SubscriberMethod> subscriberMethods = new ArrayList<>(); // 按事件型別劃分的任何方法 final Map<Class, Object> anyMethodByEventType = new HashMap<>(); // 按方法型別定義訂閱者類 final Map<String, Class> subscriberClassByMethodKey = new HashMap<>(); // 方法關鍵建造者 final StringBuilder methodKeyBuilder = new StringBuilder(128); // 訂閱的類 Class<?> subscriberClass; // clazz = subscriberClass Class<?> clazz; // 跳過父類(預設為false) boolean skipSuperClasses; // 訂閱資訊 SubscriberInfo subscriberInfo; void initForSubscriber(Class<?> subscriberClass) { this.subscriberClass = clazz = subscriberClass; skipSuperClasses = false; subscriberInfo = null; } // 回收 void recycle() {} // 檢查新增 boolean checkAdd(Method method, Class<?> eventType) {} // 檢查新增方法的簽名 private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {} // 向上查詢 void moveToSuperclass() {} }
我們可以從類的簡化中獲取需要的資訊,這個其實就是用於儲存被訂閱類中的資訊
接下來讓我再看 SubscriberMethodFinder.prepareFindState 方法


這個方法其實就是在複用 FindState 物件,如果快取池裡面沒有,就直接 new FindState,這種方式其實是用了享元設計模式,這樣做的好處是什麼呢?因為享元設計模式中的最大一個特點就是物件複用,那麼問題又來了,為什麼要複用物件呢?因為使用 FindState 次數非常多,為了避免重複建立導致不必要的記憶體浪費,所以 EventBus 採用享元設計模式來解決這一問題
SubscriberMethod

剛剛看完了 FindState 類,再來了解一下 SubscriberMethod 這個類是做什麼的,先簡單過一遍原始碼
/** EventBus內部使用並生成訂閱者索引 */ /** Used internally by EventBus and generated subscriber indexes. */ public class SubscriberMethod { final Method method; final ThreadMode threadMode; final Class<?> eventType; final int priority; final boolean sticky; /** Used for efficient comparison */ String methodString; public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) { this.method = method; this.threadMode = threadMode; this.eventType = eventType; this.priority = priority; this.sticky = sticky; } @Override public boolean equals(Object other) { if (other == this) { return true; } else if (other instanceof SubscriberMethod) { checkMethodString(); SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other; otherSubscriberMethod.checkMethodString(); // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6 return methodString.equals(otherSubscriberMethod.methodString); } else { return false; } } private synchronized void checkMethodString() { if (methodString == null) { // Method.toString has more overhead, just take relevant parts of the method StringBuilder builder = new StringBuilder(64); builder.append(method.getDeclaringClass().getName()); builder.append('#').append(method.getName()); builder.append('(').append(eventType.getName()); methodString = builder.toString(); } } @Override public int hashCode() { return method.hashCode(); } }
同樣的套路,我們先把類簡化一下
public class SubscriberMethod { // 反射的方法 final Method method; // 執行緒排程 final ThreadMode threadMode; // 引數型別 final Class<?> eventType; // 優先順序 final int priority; // 是否為粘性事件 final boolean sticky; public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) { this.method = method; this.threadMode = threadMode; this.eventType = eventType; this.priority = priority; this.sticky = sticky; } }
這下真相大白了,這個 SubscriberMethod 就是專門記錄被 @Subscribe 註解方法的一些資訊,而 FindState 是專門記錄訂閱到 EventBus 中物件的一些資訊。簡單點來說,SubscriberMethod 是記錄方法資訊,而 FindState 是專門記錄類的資訊
EventBus.register
首先我們來看看這個方法的原始碼

看不懂沒關係,我們先看一下注釋
Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they are no longer interested in receiving events. Subscribers have event handling methods that must be annotated by {@link Subscribe}. The {@link Subscribe} annotation also allows configuration like {@link ThreadMode} and priority.
註冊給定的訂閱伺服器來接收事件。訂閱者必須呼叫{@link #unregister(Object)} 不再對接收事件感興趣。 訂閱者擁有必須由{@link Subscribe}註釋的事件處理方法。 {@link Subscribe}註釋還允許像{@link ThreadMode}和priority這樣的配置。
接下來看一下 findSubscriberMethods 方法做了什麼操作

private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>(); List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { // 先判斷一下這個 class 型別之前有沒有例項註冊過 EventBus List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { // 如果有的話就不要再遍歷一次了,直接將之前遍歷的結果返回回去,提升執行效率 return subscriberMethods; } // 這個欄位正是 EventBusBuilder.ignoreGeneratedIndex 傳過來的 // 這個 EventBus 是支援編譯時註解生成的方法,類似於 Butterknife // 這個欄位一定要搞懂,不懂的一定要回去看我前面兩篇文章 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; } }
關於 ignoreGeneratedIndex 這個欄位的出處

public class EventBusBuilder { boolean ignoreGeneratedIndex; /** 翻譯:強制使用反射,即使生成了索引(預設值:false) **/ /** Forces the use of reflection even if there's a generated index (default: false). */ public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) { this.ignoreGeneratedIndex = ignoreGeneratedIndex; return this; } }
findUsingInfo
既然 ignoreGeneratedIndex 預設值為 false,那麼讓我們先看一下 findUsingInfo 方法的原始碼

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { // FindState 類,還有 prepareFindState 方法剛剛已經講過了,這裡不再贅述 FindState findState = prepareFindState(); // 重置 FindState 物件中的一些欄位,恢復到預設狀態 findState.initForSubscriber(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.moveToSuperclass(); } return getMethodsAndRelease(findState); }
讓我們一層一層解析原始碼,到底程式碼的作用是什麼
while (findState.clazz != null) { ...... findState.moveToSuperclass(); }
class SubscriberMethodFinder { static class FindState { Class<?> subscriberClass; // 是否跳過父類 Class<?> clazz; void initForSubscriber(Class<?> subscriberClass) { this.subscriberClass = clazz = subscriberClass; skipSuperClasses = false; // 不跳過父類 } void recycle() { subscriberClass = null; clazz = null; } // 讓我們簡單看一下這段程式碼 void moveToSuperclass() { if (skipSuperClasses) { clazz = null; } else { clazz = clazz.getSuperclass(); String clazzName = clazz.getName(); /** 跳過系統類,這隻會降低效能 **/ /** Skip system classes, this just degrades performance. */ if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) { clazz = null; } } } } }
這是一段經過精簡的原始碼,我們稍微看一下就能看懂,這是在查詢被訂閱物件全部的超類(父類)的被訂閱的方法,特別是 moveToSuperclass 方法已經告訴我們了,當超類是系統類的時候不會再往上查找了,我們可以回顧一下 Object 類,它的完整包名是 java.lang.Object,也就是說到了 Object 類後會直接跳出,不會被遍歷,因為 Object 類中不可能有被 @Subscribe 註解的方法,Activity 和 Fragment 也是同理
再看下一段程式碼,getSubscriberInfo 方法是塊硬骨頭,需要一點一點啃
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { // 讓我們重點看一下這段程式碼的原始碼 findState.subscriberInfo = getSubscriberInfo(findState); ...... findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
private List<SubscriberInfoIndex> subscriberInfoIndexes; // 看這個方法名就知道這個方法是幹什麼的了(獲取訂閱資訊) 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) { // 遍歷所有的索引(這裡的索引是指 MyEventBusIndex 類,前面的文章已經介紹過了) for (SubscriberInfoIndex index : subscriberInfoIndexes) { SubscriberInfo info = index.getSubscriberInfo(findState.clazz); if (info != null) { return info; } } } return null; }
// 關於 List<SubscriberInfoIndex> 欄位的來源可以看這裡,前面的文章已經講過的了 /** This class is generated by EventBus, do not edit. */ public class MyEventBusIndex implements SubscriberInfoIndex { private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; static { SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>(); putIndex(new SimpleSubscriberInfo(com.gatewang.ksl.ui.fragment.PersonFragment.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("onLoginEvent", com.gatewang.ksl.event.LoginEvent.class, ThreadMode.MAIN), new SubscriberMethodInfo("onLoginOutEvent", com.gatewang.ksl.event.LoginOutEvent.class, ThreadMode.MAIN), })); putIndex(new SimpleSubscriberInfo(com.gatewang.ksl.ui.activity.MainActivity.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("goBackHomeEvent", com.gatewang.ksl.event.GoBackHomeEvent.class, ThreadMode.MAIN), })); ......... } 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; } } }
看過這些程式碼相信你應該懂了,原來我們專案中使用 @Subscribe 註解地方的資訊都在這裡,那麼為什麼要儲存在這裡呢?好處在哪兒呢?,讓我們再回顧一下剛剛講過的 ignoreGeneratedIndex 這個欄位
public class EventBusBuilder { boolean ignoreGeneratedIndex; /** 翻譯:強制使用反射,即使生成了索引(預設值:false) **/ /** Forces the use of reflection even if there's a generated index (default: false). */ public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) { this.ignoreGeneratedIndex = ignoreGeneratedIndex; return this; } }
現在我們能明白了,為什麼預設不用強制使用反射,因為用反射來獲取類和方法的資訊比較耗效能,EventBus 出了一個 APT 自動生成程式碼外掛,將專案中帶有 @Subscribe 註解的地方儲存到 MyEventBusIndex 中,這樣我們就不需要通過反射獲取了,這樣是可以極大的提升執行效率
看完了 getSubscriberInfo 方法的作用,接下來看看剩下的程式碼
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(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.moveToSuperclass(); } return getMethodsAndRelease(findState); }
findState.subscriberInfo = getSubscriberInfo(findState); // 查詢 APT 生成的索引類有沒有儲存這個類的註解資訊 if (findState.subscriberInfo != null) { // 如果有的話 SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); // 遍歷被註解所有的方法 for (SubscriberMethod subscriberMethod : array) { // 檢查被註解方法有沒有被新增到 FindState 物件中,有的話就不要重複添加了 if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { findState.subscriberMethods.add(subscriberMethod); } } } else { // 如果沒有的話,讓我們重點看一下這段程式碼 findUsingReflectionInSingleClass(findState); }
// find Using Reflection In Single Class 翻譯: 查詢在單個類中使用反射 private void findUsingReflectionInSingleClass(FindState findState) { // 建立一個數組,用於存放被註解的方法資訊 Method[] methods; try { // 這比getMethods更快,特別是當訂閱者是像 activity 這樣的胖類時 // 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 的,並且不能為 *private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC *抽象方法,靜態方法,介面方法,合成方法 */ if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { // 獲取這個方法的引數型別 Class<?>[] parameterTypes = method.getParameterTypes(); // 這個方法的引數必須只能有一個 if (parameterTypes.length == 1) { // 獲取方法上面的註解,例如:@Subscribe(threadMode = ThreadMode.MAIN) Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); // 這個方法必須要有這個 @Subscribe 註解才會往下執行 if (subscribeAnnotation != null) { // 獲取方法引數上面的型別 Class<?> eventType = parameterTypes[0]; // 檢查有沒有被新增到 FindState 物件中,有的話就不要重複添加了 if (findState.checkAdd(method, eventType)) { // 獲取需要被呼叫的執行緒型別 ThreadMode threadMode = subscribeAnnotation.threadMode(); // 新增這個方法的註解資訊到 FindState 物件中 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"); } } }
// 簡單看一下 @Subscribe 這個註解類 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { ThreadMode threadMode() default ThreadMode.POSTING; // 預設將該方法和訊息傳送方在同一個執行緒中執行 /** * 如果為真,則傳遞最近的粘性事件(用 * {@link EventBus#postSticky(Object)}到此訂閱伺服器(如果事件可用)。 * * If true, delivers the most recent sticky event (posted with * {@link EventBus#postSticky(Object)}) to this subscriber (if event available). */ boolean sticky() default false; // 預設不是粘性訊息 /** * 訂閱者優先順序,以影響事件交付的順序。 * 在同一個交付執行緒內({@link ThreadMode}),優先順序更高的訂閱者將在之前接收事件 * 其他優先順序較低的。預設優先順序為0。注意:優先順序不影響順序 * 使用不同的{@link ThreadMode}的訂閱者之間的傳遞! * * Subscriber priority to influence the order of event delivery. * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of * delivery among subscribers with different {@link ThreadMode}s! */ int priority() default 0; // 預設優先順序為 0 }
看完了這個 findUsingInfo 方法的原始碼,我們大致瞭解了這個方法的作用和流程,其實就是在找被註解的類和方法,大致有兩種方式,一種是通過自動生成的索引類 MyEventBusIndex(由 APT 自動生成的)獲取到被註解的方法,這種方式比較高效,另一種是直接通過反射直接獲取方法,這種效率會比較低下,所以 EventBus 預設會採用第一種方式,如果沒有找到 MyEventBusIndex 類,則使用反射的方式獲取
findUsingReflection
看完了 findUsingInfo ,再看 findUsingReflection
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findUsingReflectionInSingleClass(findState); findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
你會發現 findUsingReflection 就是 findUsingInfo 精簡版,不信你自己比對一下
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(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.moveToSuperclass(); } return getMethodsAndRelease(findState); }
經過對比可以看出,findUsingReflection 沒有用 getSubscriberInfo 方法查詢索引類,而是直接通過反射獲取所需要的資訊
剛剛我們好像漏掉了一個方法 getMethodsAndRelease 沒講,這個方法在 findUsingReflection 和 findUsingInfo 的最後一行出現了,讓我們來看一下它的原始碼
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) { // 建立一個新的集合物件,將 findState.subscriberMethods 這個集合的資料新增到新建立的集合中 List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods); // 對 FindState 裡面的物件進行回收 findState.recycle(); // 將使用過的 FindState 存放到快取池中,方便下次複用 synchronized (FIND_STATE_POOL) { for (int i = 0; i < POOL_SIZE; i++) { if (FIND_STATE_POOL[i] == null) { FIND_STATE_POOL[i] = findState; break; } } } // 將這個新的集合返回回去 return subscriberMethods; }
你是否似曾感覺好像有一段程式碼是和這個方法對應的,沒錯就是 prepareFindState 方法,前面已經講過了
private FindState prepareFindState() { synchronized (FIND_STATE_POOL) { for (int i = 0; i < POOL_SIZE; i++) { FindState state = FIND_STATE_POOL[i]; if (state != null) { FIND_STATE_POOL[i] = null; return state; } } } return new FindState(); }
Subscription
在這裡加一段小插曲,介紹一下 Subscription 類的作用
final class Subscription { // 被訂閱的物件:EventBus.register(Object subscriber) final Object subscriber; // 被訂閱的方法:@Subscribe final SubscriberMethod subscriberMethod; /** * 呼叫{@link EventBus#unregister(Object)}時為false,該函式由排隊的事件交付檢查 * {@link EventBus#invokeSubscriber(PendingPost)}以防止競爭條件 * * 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; Subscription(Object subscriber, SubscriberMethod subscriberMethod) { this.subscriber = subscriber; this.subscriberMethod = subscriberMethod; active = true; } @Override public boolean equals(Object other) { if (other instanceof Subscription) { Subscription otherSubscription = (Subscription) other; return subscriber == otherSubscription.subscriber && subscriberMethod.equals(otherSubscription.subscriberMethod); } else { return false; } } @Override public int hashCode() { return subscriber.hashCode() + subscriberMethod.methodString.hashCode(); } }
從上面這些欄位可以看出來,Subscription 其實就是包裝了訂閱物件和裡面訂閱的方法資訊
subscribe
你以為 EventBus.register 的原始碼都看完了?其實就看了 findSubscriberMethods 中的兩個方法而已,讓我們繼續往下看
public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); // 讓我們重點看一下這段程式碼 synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
// 這裡使用了同步鎖,EventBus 對執行緒安全的問題處理得很好啊 synchronized (this) { // 遍歷剛剛由 findSubscriberMethods 返回的集合(存放被 @Subscribe 註解的方法資訊) for (SubscriberMethod subscriberMethod : subscriberMethods) { // 接下來再重點看一下這個方法的原始碼 subscribe(subscriber, subscriberMethod); } }
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; // 根據事件的型別存放訂閱事件集合 private final Map<Object, List<Class<?>>> typesBySubscriber; // 根據被訂閱物件存放事件型別的集合 private final Map<Class<?>, Object> stickyEvents; // 粘性事件集合 // 必須在同步塊中呼叫 // 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); } } }
EventBus.unregister
總算把 EventBus.register 看完了,接下來看一看 EventBus.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 { // 這個物件沒有被訂閱過(EventBus.register) logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }
// 遍歷事件型別集合 for (Class<?> eventType : subscribedTypes) { // 將這個被訂閱物件和所註冊的事件型別移除掉 unsubscribeByEventType(subscriber, eventType); }
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--; } } } }
總結
EventBus 有兩種方式獲取這個物件的訂閱資訊,第一種是通過 Apt 自動生成的索引類,第二種是通過反射直接獲取類中註解的方法,顯然第一種比較高效,EventBus 優先以第一種方式去獲取, 第一種獲取不到再使用第二種,如果用反射還獲取不到,就會直接丟擲異常,告訴開發者這個物件裡面沒有被 @Subscribe 註解的方法,所以這個物件不能被訂閱到 EventBus 中(EventBus.register)。
如果通過第一種或者第二種可以獲取得到的話,會將這些訂閱的資訊分別放到兩個集合中,一個集合是按照訂閱物件存放的訂閱資訊,EventBus.unregister 的時候會從這個集合中移除相應的訂閱資訊;而另一個集合是按照事件型別存放的訂閱資訊,可想而知 EventBus.post 會從這個集合獲取相應的訂閱資訊,然後反射執行這些訂閱的方法。