EventBus设计与实现分析——订阅者的注册
前面在 EventBus設(shè)計與實現(xiàn)分析——特性介紹 一文中介紹了EventBus的基本用法,及其提供的大多數(shù)特性的用法,這讓我們對EventBus為用戶提供的主要功能有了大體的了解,為我們后續(xù)理解EventBus的設(shè)計決策提供了良好的基礎(chǔ)。這里我們就開始深入到EventBus的實現(xiàn)細節(jié),先來了解其中的訂閱者注冊的過程。
訂閱者的注冊
EventBus使用了一種類似于閉包的機制來描述訂閱者。即對于EventBus而言,一個訂閱者由一個方法+該方法綁定的上下文(也就是該方法綁定的對象)+訂閱者感興趣的事件的類型來描述。具體到實現(xiàn),即是通過SubscriberMethod+SubscriberMethod這樣幾個類來描述。
我們可以看一下這幾個類的定義,首先是Subscription類:
package org.greenrobot.eventbus;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;Subscription(Object subscriber, SubscriberMethod subscriberMethod) {this.subscriber = subscriber;this.subscriberMethod = subscriberMethod;active = true;}@Overridepublic boolean equals(Object other) {if (other instanceof Subscription) {Subscription otherSubscription = (Subscription) other;return subscriber == otherSubscription.subscriber&& subscriberMethod.equals(otherSubscription.subscriberMethod);} else {return false;}}@Overridepublic int hashCode() {return subscriber.hashCode() + subscriberMethod.methodString.hashCode();} }subscriberMethod即是我們前面提到的方法,而subscriber則是上下文object。
然后是SubscriberMethod類:
package org.greenrobot.eventbus;import java.lang.reflect.Method;/** 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;}@Overridepublic 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#c6return 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 methodStringBuilder builder = new StringBuilder(64);builder.append(method.getDeclaringClass().getName());builder.append('#').append(method.getName());builder.append('(').append(eventType.getName());methodString = builder.toString();}}@Overridepublic int hashCode() {return method.hashCode();} }訂閱者的注冊過程,即是構(gòu)造SubscriberMethod,繼而構(gòu)造Subscription,并對這些對象進行存儲管理的過程。我們可以通過EventBus.register()方法的定義具體看一下在EventBus中是怎么做:
/*** Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they* are no longer interested in receiving events.* <p/>* Subscribers have event handling methods that must be annotated by {@link Subscribe}.* The {@link Subscribe} annotation also allows configuration like {@link* ThreadMode} and priority.*/public void register(Object subscriber) {Class<?> subscriberClass = subscriber.getClass();List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);synchronized (this) {for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod);}}}// Must be called in synchronized blockprivate 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.register()和EventBus.subscribe()可以看到,register的過程主要做了這樣一些事情:
這個地方查找正確的插入位置是用的順序查找的方法,估計是考慮到對于一個事件類型,訂閱者一般不會太多,而訂閱者的優(yōu)先級一般不會很分散的緣故。
EventBus使用SubscriberMethodFinder來查找一個類中所有的監(jiān)聽者方法,主要即是findSubscriberMethods()方法。
如我們前面所見,要使一個類的某個方法成為監(jiān)聽者方法,需要給該方法添加Subscribe annotation,在SubscriberMethodFinder.findSubscriberMethods()中會查找加了這個annotation的方法。但老版本的EventBus并不是通過annotation來標識一個監(jiān)聽者方法的,而是通過命名模式等方法,因而這里可以看到一種更為靈活的設(shè)計,來給用戶提供空間,以支持不同的標識監(jiān)聽者方法的方法:
class SubscriberMethodFinder {/** In newer class files, compilers may add methods. Those are called bridge or synthetic methods.* EventBus must ignore both. There modifiers are not public but defined in the Java class file format:* http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1*/private static final int BRIDGE = 0x40;private static final int SYNTHETIC = 0x1000;private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();private List<SubscriberInfoIndex> subscriberInfoIndexes;private final boolean strictMethodVerification;private final boolean ignoreGeneratedIndex;private static final int POOL_SIZE = 4;private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,boolean ignoreGeneratedIndex) {this.subscriberInfoIndexes = subscriberInfoIndexes;this.strictMethodVerification = strictMethodVerification;this.ignoreGeneratedIndex = ignoreGeneratedIndex;}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;}}由于在應(yīng)用程序的整個生命周期中,一個類的監(jiān)聽者方法總是不會變的,因而可以看到在SubscriberMethodFinder中有創(chuàng)建一個緩存METHOD_CACHE,以監(jiān)聽者類為key,以監(jiān)聽者方法的列表為值。在SubscriberMethodFinder.findSubscriberMethods()方法中會首先檢查這個緩存中是否已經(jīng)有了對應(yīng)類的監(jiān)聽者方法,如果有的話會直接返回給調(diào)用者,沒有的時候才會通過反射等機制來查找。
ignoreGeneratedIndex用來標記是否要忽略用戶定制的監(jiān)聽者方法標識方法。若忽略則直接利用反射機制,也就是Subscribe annotation機制來查找監(jiān)聽者方法,即調(diào)用findUsingReflection()方法;否則會先嘗試使用用戶定制的機制來查找,查找失敗時才會使用反射機制來查找,這也就是調(diào)用findUsingInfo()方法。
在findUsingReflection()方法中實現(xiàn)了如我們前面提到的用annotation標識監(jiān)聽者方法的方式下,查找監(jiān)聽者方法的過程:
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findUsingReflectionInSingleClass(findState);findState.moveToSuperclass();}return getMethodsAndRelease(findState);}private void findUsingReflectionInSingleClass(FindState findState) {Method[] methods;try {// This is faster than getMethods, especially when subscribers are fat classes like Activitiesmethods = findState.clazz.getDeclaredMethods();} catch (Throwable th) {// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149methods = 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");}}}在findUsingReflection()中,會借助于FindState來查找所有的監(jiān)聽者方法。FindState類的角色主要有三個,一是迭代器,用于在監(jiān)聽者類的整個繼承體系中遍歷每一個類;二是容器,用來存放找到的每一個監(jiān)聽者方法;三是對找到的方法做驗證。
findUsingReflectionInSingleClass()方法找到一個特定類中所有的監(jiān)聽者方法。這里可以清楚地看到EventBus對于監(jiān)聽者方法的聲明的完整限制。一個方法被認定為監(jiān)聽者方法的條件為:
strictMethodVerification是一個調(diào)試開關(guān),如果開關(guān)打開,則當一個類聲明了annotation Subscribe,但modifier或參數(shù)列表不滿足要求時,會直接拋出異常。
我們再來詳細地看一下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 checkthrow new IllegalStateException();}// Put any non-Method object to "consume" the existing MethodanyMethodByEventType.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 classreturn true;} else {// Revert the put, old class is further down the class hierarchysubscriberClassByMethodKey.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;}}}}moveToSuperclass()是迭代器方法,用于幫助SubscriberMethodFinder在訂閱者類的整個繼承層次中進行遍歷。這里可以看到,這個遍歷過程一般不會遍歷到Object才停止,如果訂閱者類直接或間接繼承了java標準庫或android框架層的類的話,遍歷到那些類的時候就會終止了。checkAdd(Method method, Class<?> eventType)方法施加我們前面提到的額外限制,這個過程大體為:
SubscriberMethodFinder中FindState對象并不會總是被重新創(chuàng)建,而是有一個緩沖池,需要的時候從這個池子中獲取所需得對象,而在對象用完之后,又會被重新歸還到池子中,這些操作的詳細過程可以在prepareFindState()和getMethodsAndRelease()這兩個方法中看到:
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);findState.recycle();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;}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();}findUsingInfo()和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);} ......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) {SubscriberInfo info = index.getSubscriberInfo(findState.clazz);if (info != null) {return info;}}}return null;}所謂的用戶自定義訂閱者方法聲明機制,需要通過實現(xiàn)SubscriberInfoIndex和SubscriberInfo接口來實現(xiàn),并通過EventBusBuilder在創(chuàng)建EventBus對象時傳遞進來。SubscriberInfoIndex和SubscriberInfo接口的定義為:
package org.greenrobot.eventbus.meta;/*** Interface for generated indexes.*/ public interface SubscriberInfoIndex {SubscriberInfo getSubscriberInfo(Class<?> subscriberClass); }和
package org.greenrobot.eventbus.meta;import org.greenrobot.eventbus.SubscriberMethod;/** Base class for generated index classes created by annotation processing. */ public interface SubscriberInfo {Class<?> getSubscriberClass();SubscriberMethod[] getSubscriberMethods();SubscriberInfo getSuperSubscriberInfo();boolean shouldCheckSuperclass(); }至此,EventBus中,注冊訂閱者的過程就分析完了。
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的EventBus设计与实现分析——订阅者的注册的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OkHttp3 HTTP请求执行流程分析
- 下一篇: chromium net库设计