EventBus原始碼分析(五)
簡介
前面我學習瞭如何使用EventBus,還有了解了EventBus的特性,那麼接下來我們一起來學習EventBus的原始碼,檢視EventBus的原始碼,看看EventBus給我們帶來什麼驚喜以及程式設計思想。

EventBus-Android的釋出 - 訂閱
這個圖我們從一開始就一直放置在上面了。我們在來回顧一下,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例項,如前面的例子使用索引、配置 ofollow,noindex">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`註解繫結的方法。 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); //必須線上程同步中進行。 synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
上面的程式碼表示的是給註冊者接收事件。 傳遞當前所註冊的物件,如 Activity、Fragment
。
- 注意: 註冊者如果不再接收資訊,必須呼叫
unregister(Object)
方法,表示解除註冊則該訂閱者將不再接收到資料了,如果不進行解除將可能出現記憶體洩漏。一般在onDestroy
方法解除註冊後面會講解到。 在註冊者中擁有必須由@Subscribe
註解的方法。@Subscribe
還允許配置ThreadMode
和優先順序、是否是粘性行為。
接著,我們進入 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
方法。
//private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>(); //執行緒安全 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //從Map集合中獲取訂閱方法列表 List<SubscriberMethod> 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<SubscriberMethod>
- 注:設定索引在 EventBus的配置&事件的優先順序&使用索引(四) 這裡說的,大家可以去看看。
private List<SubscriberMethod> findUsingReflection(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 List<SubscriberMethod> findUsingInfo(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<SubscriberMethod>
等.
如果當前訂閱者 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 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; //將查詢狀態器findState,新增到狀態池FIND_STATE_POOL中,為下次直接從查詢狀態池中獲取 break; } } } return subscriberMethods; // 返回訂閱方法列表 }
上面該方法通過 FindState
靜態內部類獲取了訂閱的列表,然後被儲存到了 ConcurrentHashMap
集合中。並且儲存到狀態池中,為方便下次讀取。並且回收資源。
獲取到 List<SubscriberMethod>
訂閱列表後,再次回到了註冊方法中register(Object subscriber)進入到方法 subscribe(subscriber, subscriberMethod);
該方法通過迴圈進行遍歷
- 注 此處必須是線上程同步中進行,所以新增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<Subscription>列表,該類大家可以研究一下, //是寫時複製容器,即當我們往`CopyOnWriteArrayList`容器新增元素時,先從原有的陣列中拷貝一份出來,然後在新的容器做寫操作(新增元素),寫完之後,再將原來的陣列引用指向到新陣列的形式儲存資料的,它是執行緒安全的。 CopyOnWriteArrayList<Subscription> 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<Class<?>> 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<Class>). Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, 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<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); //新增到事件佇列中 //判斷當前傳送執行緒狀態是否是正在傳送的事件 if (!postingState.isPosting) { postingState.isMainThread = isMainThread(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } try { while (!eventQueue.isEmpty()) { //傳送事件,傳送完一個刪除一個,直到傳送全部停止迴圈 postSingleEvent(eventQueue.remove(0), postingState); } } finally { //設定標誌改變 postingState.isPosting = false; postingState.isMainThread = false; } } }
currentPostingThreadState.get()
方法中我們知道
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() { @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<Class<?>> 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<Class<?>>
,然後進行遍歷進行或運算。
我們先進入 lookupAllEventTypes(eventClass)
方法,看看返回的事件型別,並且知道他儲存到Map集合中,即key是當前事件class,value是該事件的所有型別,包括父類,介面等。
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */ private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) { //private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>(); key是當前事件型別的class,valu則是事件的所有型別,包括父類,介面等。本事例會得到的是MessageEvent.class、和Object.class synchronized (eventTypesCache) { List<Class<?>> 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) { CopyOnWriteArrayList<Subscription> subscriptions; 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>
在這裡就用到了,通過傳送事件的 class
獲取 CopyOnWriteArrayList
容器裡面的訂閱事件資訊,包括註冊者物件,訂閱方法等。然後遍歷並進行傳送事件。在此方法釋出 postToSubscription()
事件
/** * 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<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()); } }
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */ /** * subscriber:指代當前解除註冊的物件如Activity, * eventType:傳送的事件型別,post函式的實體物件class */ 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--; } } } }
解除繫結,這裡很簡單。這裡就不一一進行解釋了, _ 。
總結
1.本篇文章主要對EventBus原始碼進行簡單的分析。主要是進行了觀察者模式的一些高階運用。如果大家對觀察者模式理解不怎麼清楚可以進入這裡看看簡單的案例 觀察者模式 ,內容非常簡單。
2.相關的EvenBut的使用,請看之前的內容。如 EventBus認識(一) 、 EventBus的ThreadMode使用以及分析(二) 等等。
3.學習本篇文章中可以認識到一些常用的類如 CopyOnWriteArrayList
、 ThreadLocal
等,到時可以深入研究一下,有助我們提高。
4.一些程式設計思想,設計模式值得我們去學習的,如單例模式EventBus雙重校驗、建造者模式,EventBus構造器的時候用到,用了初始化各個引數等等。面向介面程式設計而不針對實現程式設計。
5.如果有什麼問題希望大家進行指正,好好學習,一起進步。