日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

EventBus设计与实现分析——订阅者的注册

發(fā)布時間:2024/4/11 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 EventBus设计与实现分析——订阅者的注册 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前面在 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īng)用程序的一次生命周期中,這些方法總是不變的。
  • 構(gòu)造Subscription來描述每一個訂閱者,也就是訂閱者對象+一個訂閱者方法。
  • 建立事件類型與訂閱者之間的映射關(guān)系,這主要通過subscriptionsByEventType數(shù)據(jù)結(jié)構(gòu)來描述。對于每一個事件類型,其訂閱者會依優(yōu)先級而排序。不難看出,這個結(jié)構(gòu)主要用于事件的發(fā)布,在事件發(fā)布時,可以通過這個結(jié)構(gòu)快速地找到每一個訂閱者。
    這個地方查找正確的插入位置是用的順序查找的方法,估計是考慮到對于一個事件類型,訂閱者一般不會太多,而訂閱者的優(yōu)先級一般不會很分散的緣故。
  • 建立訂閱者對象與該對象監(jiān)聽的事件類型之間的映射關(guān)系,這主要由typesBySubscriber數(shù)據(jù)結(jié)構(gòu)來描述。這個結(jié)構(gòu)則主要用于注銷一個對象對事件的監(jiān)聽。注銷一個對象對事件得監(jiān)聽時,可以通過這個結(jié)構(gòu)快速地將監(jiān)聽者對象得信息從EventBus中整個的移除出去。
  • 處理對Sticky事件的監(jiān)聽。如我們前面所見,這種類型的事件在發(fā)布時會通知到每個監(jiān)聽者,同時還會被保存起來,當后面再次注冊對這種事件的監(jiān)聽時,監(jiān)聽者會直接得到通知。
  • 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)聽者方法的條件為:

  • 方法的可見性被聲明為全局可見public,同時沒有abstract、static、bridge及synthetic修飾。
  • 方法的參數(shù)只有一個。
  • 有annotation Subscribe的聲明。
  • FindState.checkAdd()會實施一些額外的限制。主要是關(guān)于,類中聲明了多個方法監(jiān)聽相同事件的處理;及方法override的處理,即在類的繼承體系中,有多個類聲明了相同的方法,對相同的事件進行監(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)方法施加我們前面提到的額外限制,這個過程大體為:

  • 訂閱者類的整個繼承層次中只有一個方法訂閱了某個特定類型的事件,則該方法直接被認定為訂閱者方法。
  • 繼承層次中對同一個事件類型有多個訂閱者方法的,只要不同方法的方法名不同,則都會被認定為訂閱者方法。
  • 繼承層次中某個被override的方法被聲明了訂閱者方法,則繼承層次中深度最深的類的方法被認定為訂閱者方法。子類比父類在繼承層次中的深度要深。
  • 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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。