摘要:進入源碼分析我們從的注冊開始入手。關閉此功能將改善事件的發布。創建線程池我們將此段代碼逐步分析這步主要是進行初始化話一下必要的參數,如代碼注解所示。必須在線程同步中進行。必須保持線程安全的,所以這里使用了。
簡介
前面我學習了如何使用EventBus,還有了解了EventBus的特性,那么接下來我們一起來學習EventBus的源碼,查看EventBus的源碼,看看EventBus給我們帶來什么驚喜以及編程思想。
這個圖我們從一開始就一直放置在上面了。我們在來回顧一下,EventBus的官網是怎么定義它的呢?
EventBus是Android和Java的發布/訂閱(觀察者模式)事件總線。
我們大概了解了EventBus的構建思想。接下來我們進入源碼學習吧。
我們從EventBus的注冊開始入手。
EventBus.getDefault().register(this);
public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { //創建了EventBus實例,進入下面的方法 defaultInstance = new EventBus(); } } } return defaultInstance; }
上面的方法是一個雙重校驗的單例。
public EventBus() { this(DEFAULT_BUILDER); //private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder(); }
DEFAULT_BUILDER是EventBusBuilder.class實例來創建的,這個類非常重要:使用自定義參數創建EventBus實例,還允許自定義默認EventBus實例,如前面的例子使用索引、配置EventBus的配置&事件的優先級&使用索引(四)等就是通過這個類來實現的,大家可以回顧一下。進入初始化一下必要的參數的構造方法EventBus(EventBusBuilder builder),如下
EventBus(EventBusBuilder builder) { logger = builder.getLogger(); /初始化Logger subscriptionsByEventType = new HashMap<>(); //存儲訂閱事件的 typesBySubscriber = new HashMap<>(); //存儲相關訂閱者class列表,key是注冊者的對象,value是訂閱者的class列表 stickyEvents = new ConcurrentHashMap<>(); //粘性事件 //下面這4個實例就是我們設置方法``threadMode = ThreadMode.xxx`` //*1 mainThreadSupport = builder.getMainThreadSupport(); mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; backgroundPoster = new BackgroundPoster(this); asyncPoster = new AsyncPoster(this); //獲取注冊者信息索引(添加由EventBus的注釋預處理器生成的索引) indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0; //初始訂閱方法查找器。這個類主要具有查找訂閱的方法,繼續往下看 subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex); logSubscriberExceptions = builder.logSubscriberExceptions; //是否有日記訂閱,默認true logNoSubscriberMessages = builder.logNoSubscriberMessages;//是否有日記信息,默認 true sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;//是否發送訂閱異常事件,默認true sendNoSubscriberEvent = builder.sendNoSubscriberEvent; //是否發送沒有訂閱事件,默認true throwSubscriberException = builder.throwSubscriberException; //是否拋出異常處理 //默認情況下,EventBus會考慮事件類層次結構(將通知超類的注冊者)。 關閉此功能將改善事件的發布。對于直接擴展Object的簡單事件類,我們測量事件發布的速度提高了20%。 eventInheritance = builder.eventInheritance; executorService = builder.executorService; //創建線程池 }
我們將此段代碼逐步分析.
這步主要是進行初始化話一下必要的參數,如代碼注解所示。
下面這段代碼就是我們常用的@Subscribe(threadMode = ThreadMode.xxx);初始化。一般常用的就是以下4種。
mainThreadSupport = builder.getMainThreadSupport(); mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; backgroundPoster = new BackgroundPoster(this); asyncPoster = new AsyncPoster(this);
我們來看看builder.getMainThreadSupport()方法返回的是MainThreadSupport接口,表示為支持Android主線程。
上面的4個線程中都持有 PendingPostQueue 等待發送的隊列實例。
由mainThreadSupport.createPoster(this)創建一個HandlerPoster而該類繼承了Handle,并且初始化了一個等待發布隊列。代碼如下。
public interface MainThreadSupport { boolean isMainThread(); Poster createPoster(EventBus eventBus); class AndroidHandlerMainThreadSupport implements MainThreadSupport { private final Looper looper; public AndroidHandlerMainThreadSupport(Looper looper) { this.looper = looper; } @Override public boolean isMainThread() { return looper == Looper.myLooper(); } @Override public Poster createPoster(EventBus eventBus) { return new HandlerPoster(eventBus, looper, 10); } } }
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex);
上面代碼在這里主要初始化訂閱的方法查找器。下面會講解到它是如何進行訂閱方法的。
我們這這里知道了EventBus初始化,然后相關的實例的創建,接下來我我們進入到register(this)方法的調用如下方法。
public void register(Object subscriber) { //獲取當前注冊者對象的Class。 Class> subscriberClass = subscriber.getClass(); //通過該注冊者的`Class`來獲取當前注冊者里面由`@Subscriber`注解綁定的方法。 ListsubscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); //必須在線程同步中進行。 synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
上面的代碼表示的是給注冊者接收事件。 傳遞當前所注冊的對象,如Activity、Fragment。
注意: 注冊者如果不再接收信息,必須調用unregister(Object)方法,表示解除注冊則該訂閱者將不再接收到數據了,如果不進行解除將可能出現內存泄漏。一般在onDestroy方法解除注冊后面會講解到。 在注冊者中擁有必須由@Subscribe注解的方法。@Subscribe還允許配置ThreadMode和優先級、是否是粘性行為。
接著,我們進入List
//private static final Map, List > METHOD_CACHE = new ConcurrentHashMap<>(); //線程安全 List findSubscriberMethods(Class> subscriberClass) { //從Map集合中獲取訂閱方法列表 List subscriberMethods = METHOD_CACHE.get(subscriberClass); //判斷當前獲取方法是否為空 if (subscriberMethods != null) { return subscriberMethods; } //通過EventBusBuilder.ignoreGeneratedIndex //是否忽略配置索引,默認是忽略索引 if (ignoreGeneratedIndex) { //1.通過反射獲取方法列表 subscriberMethods = findUsingReflection(subscriberClass); } else { //2.通過索引方式獲取訂閱方法列表 subscriberMethods = findUsingInfo(subscriberClass); } //是否訂閱方法為空,如果為空則表示該訂閱者里面的方法都不符合EventBus訂閱方法的規則 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; } }
我們逐步分析該段代碼。
該段代碼通過注冊者的類來獲取當前dingy的方法列表,如果不為空則直接返回訂閱方法。
通過反射來查找訂閱方法,如果該方法為空則拋出異常:該訂閱者的方法和其超類沒有@Subscriber注解的共有方法(即表示不符合EventBus訂閱方法的規則)。如果不為空則存儲到ConcurrentHashMap集合中。
這里的是由ConcurrentHashMap集合存儲是以當前訂閱者的class為key,而將其由@Subscriber綁定的方法添加到List中,,就是集合的value。必須保持線程安全的,所以這里使用了ConcurrentHashMap。
1.是否忽略索引設置,該方法表示忽略設置索引,進入該方法findUsingReflection(Class> subscriberClass),通過反射進行獲取List
注:設置索引在EventBus的配置&事件的優先級&使用索引(四)這里說的,大家可以去看看。
private ListfindUsingReflection(Class> subscriberClass) { FindState findState = prepareFindState();//準備查找狀態 findState.initForSubscriber(subscriberClass);// 初始化訂閱者信息 while (findState.clazz != null) {//循環獲取訂閱者的class是否為空 //通過反射獲取訂閱方法, // 3.該方法下面分析 findUsingReflectionInSingleClass(findState); findState.moveToSuperclass(); //刪除超類class } ////獲取訂閱方法list return getMethodsAndRelease(findState); }
上面的代碼主要是通過反射來獲取相關的訂閱方法,里面由靜態內部類FindState進行管理相關信息,由于上面的方法和下面索引的方法都將調用同一個方法,所以放在下面來將講解,請看下面信息。
2.是否忽略索引設置,該方法表示的設置了索引,進入findUsingInfo(Class> subscriberClass)方法,如下。
private ListfindUsingInfo(Class> subscriberClass) { FindState findState = prepareFindState(); //準備查找狀態 findState.initForSubscriber(subscriberClass);// 初始化訂閱者信息 while (findState.clazz != null) { //循環獲取訂閱者的class是否為空 findState.subscriberInfo = getSubscriberInfo(findState);//獲取獲取訂閱者信息,這里如果使用添加索引的話,這里將能獲取到索引的相關信息。 if (findState.subscriberInfo != null) { //獲取訂閱方法 SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); for (SubscriberMethod subscriberMethod : array) { //進行等級檢查,并存儲到Map集合中,該方法checkAdd是FindState.class中的方法 ,key是@Subscriber中參數的class,value是該subscriberMethod.method,也就是@Subscribe中的方法如:public void onMessageEvent(MessageEvent event) if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { //將該訂閱方法添加到列表中 findState.subscriberMethods.add(subscriberMethod); } } } else { //3.通過使用反射的方式來獲取,和上面忽略索引調用到同一個方法,這里將解析 findUsingReflectionInSingleClass(findState); } //清除父類的方法 findState.moveToSuperclass(); } //獲取訂閱方法list return getMethodsAndRelease(findState); }
分析本段代碼。
首先進入查找的準備狀態,通過默認狀態池返回狀態信息,主要存儲的是FindState實例。主要存儲的有訂閱者的class、訂閱者信息SubscriberInfo。訂閱者@Subscribe方法列表List
如果當前訂閱者class不為空,則將獲取到訂閱者信息。這里分兩種情況。
如果使用添加索引的話,getSubscriberInfo(findState)該方法將獲取到訂閱信息,如果沒有使用索引的話則將調用findUsingReflectionInSingleClass(findState);該方法來獲取信息
使用添加索引,并返回訂閱方法,該方法如下
//獲取訂閱方法 SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
由于AbstractSubscriberInfo類實現了SubscriberInfo接口,而SimpleSubscriberInfo繼承了AbstractSubscriberInfo并實現了getSubscriberMethods方法,代碼如下:
本段代碼在SimpleSubscriberInfo.class中。返回訂閱方法數組SubscriberMethod[]。
@Override public synchronized SubscriberMethod[] getSubscriberMethods() { int length = methodInfos.length; SubscriberMethod[] methods = new SubscriberMethod[length]; for (int i = 0; i < length; i++) { SubscriberMethodInfo info = methodInfos[i]; methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode, info.priority, info.sticky); } return methods; }
3.如果沒有添加索引則進入該方法findUsingReflectionInSingleClass(findState);
通過反射來獲取訂閱者方法。這個方法有點長,慢慢看。
private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities //該方法代替getMethods()方法,獲取該訂閱者class全部方法。 methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 methods = findState.clazz.getMethods(); //獲取該訂閱者class全部方法 findState.skipSuperClasses = true; //設置跳過超類 } for (Method method : methods) { int modifiers = method.getModifiers();//獲取修飾符 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //通過與運算判斷當前的修飾符,是否符合EventBus方法定義 Class>[] parameterTypes = method.getParameterTypes(); //獲取參數類型 if (parameterTypes.length == 1) { //因為EventBus方法中必須有參數,所以當參數為1時,符合要求 //通過注解的形式@Subscribe獲取Subscribe,默認的Subscribe為(priority==0,sticky=false,threadMode=POSTING),就是說ThreadMode為POSTING,粘性為false,優先級為0,如果方法中設置了相應的值,則將是你設置的值。如threadMode=MAIN。 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { Class> eventType = parameterTypes[0]; //獲取當前參數的class //進行等級檢查,并存儲到Map集合中,該方法checkAdd是FindState.class中的方法 ,key是@Subscriber中參數的class,value是該subscriberMethod.method,也就是@Subscribe中的方法如:public void onMessageEvent(MessageEvent event) if (findState.checkAdd(method, eventType)) { // 獲取ThreadMode ,如MAIN ThreadMode threadMode = subscribeAnnotation.threadMode(); //將訂閱方法添加到列表中 findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { //不符合EventBus訂閱方法的規則要求。拋出異常,提示該方法必須是@Subscribe注解,并且需要一個參數 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)) {//是一種非法的@Subscribe方法:必須是公共的,非靜態的,非抽象的 String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); } } }
該段代碼主要是獲取該注冊者的全部方法,并進行篩選出來符合EventBus的訂閱方法的規則,通過注解的形式來獲取訂閱方法,最后添加到查找狀態的列表中。
首先獲取的是修飾符,參數類型,再通過注解的形式來獲取訂閱方法。如下注解Subscribe ,如果不符合EventBus相關訂閱方法的規則將拋出異常提示,是否在方法上書寫了錯誤的方法。
接下來我們進入看看@Subscribe 訂閱方法是通過注解的形式來設置的。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { //ThreadMode ,默認POSTING ThreadMode threadMode() default ThreadMode.POSTING; //粘性事件,默認false boolean sticky() default false; //優先級,默認0 int priority() default 0; }
接下來進入getMethodsAndRelease(FindState findState)
private ListgetMethodsAndRelease(FindState findState) { //獲取到訂閱方法列表 List 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; //將查找狀態器findState,添加到狀態池FIND_STATE_POOL中,為下次直接從查找狀態池中獲取 break; } } } return subscriberMethods; // 返回訂閱方法列表 }
上面該方法通過FindState靜態內部類獲取了訂閱的列表,然后被存儲到了ConcurrentHashMap集合中。并且存儲到狀態池中,為方便下次讀取。并且回收資源。
獲取到List
注 此處必須是在線程同步中進行,所以添加synchronized。
//這里的參數subscriber表示的是訂閱者,如Activity等,subscriberMethod表示訂閱方法,該方法就是在subscriber里面 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class> eventType = subscriberMethod.eventType; //獲取事件類型,也就是參數的class Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //通過事件類型參數class獲取CopyOnWriteArrayList列表,該類大家可以研究一下, //是寫時復制容器,即當我們往`CopyOnWriteArrayList`容器添加元素時,先從原有的數組中拷貝一份出來,然后在新的容器做寫操作(添加元素),寫完之后,再將原來的數組引用指向到新數組的形式存儲數據的,它是線程安全的。 CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); //添加到Map集合中 } 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);//優先級高的添加添加到CopyOnWriteArrayList容器最前面,只有一個的時候就是添加在第一位了。 break; } } //獲取訂閱事件的class列表,即訂閱方法的參數class列表 List > subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<>(); //存儲到Map集合,key是訂閱者(即上面傳遞的Activity對象),value是訂閱事件列表 typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); //將該事件類型class添加到訂閱事件列表中 判斷當前方法是否是粘性的 if (subscriberMethod.sticky) { if (eventInheritance) { //必須考慮eventType的所有子類的現有粘性事件 // 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 ). Set , Object>> entries = stickyEvents.entrySet(); for (Map.Entry , Object> entry : entries) { Class> candidateEventType = entry.getKey(); isAssignableFrom(Class> cls)方法表示的類型是否可以通過標識轉換或通過擴展引用轉換為此類對象表示的類型,則返回true if (eventType.isAssignableFrom(candidateEventType)) { Object stickyEvent = entry.getValue(); //將粘性事件添加到訂閱者列表中 checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { //將粘性事件添加到訂閱者列表中 Object stickyEvent = stickyEvents.get(eventType); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } }
上面的方法中主要是通過獲取"事件類型"即訂閱方法中的參數class。然后將其添加到Map集合中,這里用到了CopyOnWriteArrayList(原理:是寫時復制容器,即當我們往CopyOnWriteArrayList容器添加元素時,先從原有的數組中拷貝一份出來,然后在新的容器做寫操作(添加元素),寫完之后,再將原來的數組引用指向到新數組的形式存儲數據的,它是線程安全的。)
然后進行判斷是否設置優先級,遍歷并添加到subscriptions列表中。
通過當前的注冊者,獲取注冊者class列表,并添加到typesBySubscriber集合中,key是注冊者的class,如:Activity,而value就是訂閱方法中的參數class的列表。這個地方說明了,一個注冊者,可以擁有多個訂閱事件(方法),將其綁定起來,存儲到Map集合中。最后將該參數class添加到subscribedEvents.add(eventType);列表中。
下面方法是檢查粘性事件訂閱發送事件方法,如下
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { if (stickyEvent != null) { // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) // --> Strange corner case, which we don"t take care of here. //這段代碼我們到post發送事件的調用的時候講解。 postToSubscription(newSubscription, stickyEvent, isMainThread()); } }
注:這里主要是檢查是否發送粘性發送事件,方法 postToSubscription我們到發送事件POST方法調用時講解。
事件發送
接下來我進入post方法,事件發送。此時,我們回憶一下簡介中的圖:
post()==>EventBus==>將分發到各個訂閱事件中。也就是訂閱方法。我們來看看是如何進行操作的。代碼如下
EventBus.getDefault().post(new MessageEvent(message));
通過以上代碼我大概的知道,post()方法里面的參數是一個實體的對象,而該實體就是我們在訂閱方法中的參數實體。你發現了什么了嗎?
前邊我們已經分析了,該注冊者的訂閱方法主要的存儲過程,接下來我們一起進入探究吧。方法跟蹤進入post()方法。
/** Posts the given event to the event bus. */ //給訂閱事件,發送事件總線 public void post(Object event) { //獲取當前線程,即通過ThreadLocal本地線程來管理對應的事件線程,并且初始化發送線程狀態PostingThreadState PostingThreadState postingState = currentPostingThreadState.get(); List
currentPostingThreadState.get()方法中我們知道
private final ThreadLocalcurrentPostingThreadState = new ThreadLocal () { @Override protected PostingThreadState initialValue() { return new PostingThreadState(); } };
通過ThreadLocal這里就叫"本地線程"吧,來管理當前的發送線程狀態,每個發送線程將得到對應一個PostingThreadState,而該PostingThreadState管理eventQueue信息,該信息主要存儲的是發送事件。
通過eventQueue.add(event)存儲該發送事件以后完成以后,就判斷當前的發送事件狀態是否是正在發送中,如果還沒發送則當該eventQueue不為為空的時候,進入循環發送該事件,發送完一個就刪除一個,直到發送完成為止。
進入方法postSingleEvent(Object event, PostingThreadState postingState)中。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class> eventClass = event.getClass(); boolean subscriptionFound = false; if (eventInheritance) { //查找所有事件類型 List> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); 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)); } } }
上面的方法首先獲取該事件類型的class然后傳遞到lookupAllEventTypes(eventClass)方法中,通過該方法獲取當前發送事件的class,包括父類、實現的接口等等列表。所以一般情況下會返回當前發送的事件,和Object.class(注:當然了,如果該事件實現接口的話,也會包括實現的接口的。)列表即List
我們先進入lookupAllEventTypes(eventClass)方法,看看返回的事件類型,并且知道他存儲到Map集合中,即key是當前事件class,value是該事件的所有類型,包括父類,接口等。
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */ private static List> lookupAllEventTypes(Class> eventClass) { //private static final Map , List >> eventTypesCache = new HashMap<>(); key是當前事件類型的class,valu則是事件的所有類型,包括父類,接口等。本事例會得到的是MessageEvent.class、和Object.class synchronized (eventTypesCache) { List > eventTypes = eventTypesCache.get(eventClass); if (eventTypes == null) { eventTypes = new ArrayList<>(); Class> clazz = eventClass; while (clazz != null) { eventTypes.add(clazz); //添加到列表中 //添加接口 addInterfaces(eventTypes, clazz.getInterfaces()); clazz = clazz.getSuperclass(); //獲取父類class } eventTypesCache.put(eventClass, eventTypes); //存儲d } return eventTypes; } }
看看方法postSingleEventForEventType(event, postingState, clazz),發送事件
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) { CopyOnWriteArrayListsubscriptions; synchronized (this) { //通過當前的事件class,獲取訂閱事件列表 subscriptions = subscriptionsByEventType.get(eventClass); } if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { //對PostingThreadState進行賦值 postingState.event = event; 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; }
大家是否還記得在注冊的時候出現的CopyOnWriteArrayList
/** * subscription:發布訂閱的事件處理器 * event:當前發布的事件 * isMainThread:是否是在主線程中 */ 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 { mainThreadPoster.enqueue(subscription, event); } break; case MAIN_ORDERED: if (mainThreadPoster != null) { mainThreadPoster.enqueue(subscription, event); } else { // temporary: technically not correct as poster not decoupled from subscriber 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); } }
好了,這就是之前我們在注冊中有一個地方遺留下的,就是判斷是否是粘性狀態是調用的方法,大家往回看,就看到了在方法checkPostStickyEventToSubscription中有調用到該方法。
這里會根據,在方法中所選的threadMode進行調用,
1.MAIN,如果當前是否是UI線程,則會進入到invokeSubscriber(subscription, event);方法中。否則將調用mainThreadPoster.enqueue(subscription, event);這個方法將交給HandlerPoster.class里面的enqueue(Subscription subscription, Object event)方法,HandlerPoster.class現實了該接口,移交給handle完成,大家可以進入HandlerPoster.class看看,這里就不說了。
2.POSTING直接調用 invokeSubscriber(subscription, event);
3.MAIN_ORDERED如果當前mainThreadPoster不為空則調用 mainThreadPoster.enqueue(subscription, event);和MAIN進入HandlerPoster.class,反之調用invokeSubscriber(subscription, event);
4.BACKGROUND如果當前是主線程,則調用backgroundPoster.enqueue(subscription, event);,反之調用invokeSubscriber(subscription, event);
5.ASYNC,異步方法調用,執行 asyncPoster.enqueue(subscription, event);,方法進行跟蹤,該類有實現了Runnable接口,然后調用eventBus.invokeSubscriber(pendingPost);,最終將調用了invoke方法。
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); } }
invoke()這是一個本地的方法,將調用C/C++的到方法進行發布。最后將進入到訂閱方法中。并傳遞該事件到到訂閱的方法中。
好了,這里我們將post發布事件的方法簡單的分析了一下。整個EventBus的分析差不多完成了,但是,我們還有一點不能忘記就是解除綁定。接下來我們來看看解除綁定的方法。
注:該方法一般生命周期的onDestroy()方法中進行解除綁定。
代碼如下:
EventBus.getDefault().unregister(this);
直接進入unregister(this)
/** Unregisters the given subscriber from all event classes. */ public synchronized void unregister(Object subscriber) { List> 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()); } }
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */ /** * subscriber:指代當前解除注冊的對象如Activity, * eventType:發送的事件類型,post函數的實體對象class */ private void unsubscribeByEventType(Object subscriber, Class> eventType) { Listsubscriptions = 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--; } } } }
解除綁定,這里很簡單。這里就不一一進行解釋了,^_^。
總結1.本篇文章主要對EventBus源碼進行簡單的分析。主要是進行了觀察者模式的一些高級運用。如果大家對觀察者模式理解不怎么清楚可以進入這里看看簡單的案例觀察者模式,內容非常簡單。
2.相關的EvenBut的使用,請看之前的內容。如EventBus認識(一)、EventBus的ThreadMode使用以及分析(二)等等。
3.學習本篇文章中可以認識到一些常用的類如CopyOnWriteArrayList、ThreadLocal等,到時可以深入研究一下,有助我們提高。
4.一些編程思想,設計模式值得我們去學習的,如單例模式EventBus雙重校驗、建造者模式,EventBus構造器的時候用到,用了初始化各個參數等等。面向接口編程而不針對實現編程。
5.如果有什么問題希望大家進行指正,好好學習,一起進步。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71876.html
摘要:音樂團隊分享數據綁定運行機制分析一個項目搞定所有主流架構單元測試一個項目搞定所有主流架構系列的第二個項目。代碼開源,展示了的用法,以及如何使用進行測試,還有用框架對的進行單元測試。 Android 常用三方框架的學習 Android 常用三方框架的學習 likfe/eventbus3-intellij-plugin AS 最新可用 eventbus3 插件,歡迎品嘗 簡單的 MVP 模...
閱讀 2166·2021-09-04 16:40
閱讀 1460·2021-08-13 15:07
閱讀 3608·2019-08-30 15:53
閱讀 3198·2019-08-30 13:11
閱讀 1075·2019-08-29 17:22
閱讀 1815·2019-08-29 12:47
閱讀 1478·2019-08-29 11:27
閱讀 2229·2019-08-26 18:42