EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!
[EventBus原始碼分析(一):入口函式提綱挈領(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51802172)
[EventBus原始碼分析(二):register方法儲存事件的訂閱者列表(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51819508)
[EventBus原始碼分析(三):post方法釋出事件【獲取事件的所有訂閱者,反射呼叫訂閱者事件處理方法】(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51821143)
[EventBus原始碼分析(四):執行緒模型分析(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51832001)
[EventBus原始碼解讀詳細註釋(1)register的幕後黑手](http://blog.csdn.net/wangshihui512/article/details/50914817)
[EventBus原始碼解讀詳細註釋(2)MainThread執行緒模型分析](http://blog.csdn.net/wangshihui512/article/details/50934012)
[EventBus原始碼解讀詳細註釋(3)PostThread、MainThread、BackgroundThread、Async四種執行緒模式的區別](http://blog.csdn.net/wangshihui512/article/details/50935729)
[EventBus原始碼解讀詳細註釋(4)register時重新整理的兩個map](http://blog.csdn.net/wangshihui512/article/details/50938663)
[EventBus原始碼解讀詳細註釋(5)事件訊息繼承性分析 eventInheritance含義](http://blog.csdn.net/wangshihui512/article/details/50947102)
[EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!](http://blog.csdn.net/wangshihui512/article/details/50949960)
EventBus內部儲存了事件到此事件所有訂閱者的map,因此post事件的時候,可以找到訂閱了此事件的所有訂閱者,然後根據訂閱者對此事件的執行緒模型,在對應的執行緒裡邊,通過反射呼叫事件處理函式。
先看EventBus裡邊兩個重要的map
/*下邊的兩個map的資料都是各個執行緒都可以訪問的,因此訪問的時候要對這兩個map加鎖*/ /*事件到訂閱者列表的map,key是事件,也就是訊息處理方法的引數的Class,value是所有的訂閱此事件的訂閱者列表*/ private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; /*訂閱者到訂閱者訂閱的所有事件列表的map,key是訂閱者,value是該訂閱者訂閱的所有事件的列表*/ private final Map<Object, List<Class<?>>> typesBySubscriber;正是這兩個map儲存了事件和訂閱者之間對應關係的重要資訊。
然後從原始碼的角度檢視post事件的時候,事件處理方法是如何被呼叫的
/**EventBus維護了兩個重要的map,其中一個就是事件到所有訂閱了此事件的訂閱者所構成的列表的map
* 因此post一個事件,總能根據此map找到所有的訂閱者,再根據訂閱者處理此事件的執行緒模型,將此事件
* 分發到對應的執行緒,利用反射,呼叫訂閱者對此事件的事件處理方法,完成事件的釋出與處理
* Posts the given event to the event bus.
* @param event*/
public void post(Object event) {
/* private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>()
* ThreadLocal,每個執行緒獨有,當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,
* 所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。*/
/*獲取釋出者執行緒的PostingThreadState,PostingThreadState封裝了一些釋出者執行緒的資料*/
PostingThreadState postingState = currentPostingThreadState.get();
/*獲取該釋出者執行緒的事件列表*/
List<Object> eventQueue = postingState.eventQueue;
/*將新post的event新增到釋出者執行緒的事件列表*/
eventQueue.add(event);
/*判斷標誌位,判斷髮布者執行緒是否正在post*/
if (!postingState.isPosting) {
/** Looper.getMainLooper():
* Returns the application's main looper, which lives in the main thread of the application.
* Looper.myLooper:
* Return the Looper object associated with the current thread.
*/
/*判斷髮布者執行緒是否為UI執行緒*/
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
/*設定標誌位,防止重複進入。假如釋出者正在釋出post,此時釋出者又post了一個事件
* 雖然這時候不會通過 if (!postingState.isPosting)的判斷
* 但是eventQueue.add(event);已經把此事件加入列表,在下邊的迴圈中還是可以處理此event*/
postingState.isPosting = true;
/*如果這時候釋出者取消釋出,就丟擲異常*/
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
/*迴圈post事件,將釋出者釋出的所有事件都發布出去
* 比如在某個Activity裡邊post了好幾個事件,每次post都會將事件入佇列
* */
try {
while (!eventQueue.isEmpty()) {
/** eventQueue.remove(0):
* Removes the object at the specified location from this {@code List}.
* @param location the index of the object to remove.
* @return the removed object.
* 取隊首事件*/
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
/*設定標誌位,不管是釋出的時候出現問題,還是釋出者的所有事件釋出完畢,都會執行
* 設定正在釋出狀態為false,設定釋出者執行緒為主執行緒標誌為false
* 如果釋出者此後又post了一個事件,那麼就把此事件加入佇列
* 重新判斷髮布者執行緒是否為主執行緒*/
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
PostingThreadState封裝了一些釋出者執行緒的資料狀態
/** For ThreadLocal, much faster to set (and get multiple values). */
/*封裝了訂閱者、事件、事件佇列、和三個標誌位*/
final static class PostingThreadState {
/*釋出者執行緒釋出的事件列表,這裡已經為列表分配了記憶體,所以直接呼叫add不會導致空指標*/
final List<Object> eventQueue = new ArrayList<Object>();
/*釋出者是否正在釋出事件*/
boolean isPosting;
/*釋出者執行緒是否是主執行緒*/
boolean isMainThread;
/*釋出事件的訂閱者*/
Subscription subscription;
/*待發布的事件*/
Object event;
/*是否取消釋出*/
boolean canceled;
}
/**
* @param event
* @param postingState
* @throws Error
*/
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
/*獲取事件的Class,所有事件的Class對應的訂閱者列表在register的時候是已經儲存了的*/
Class<?> eventClass = event.getClass();
/*該事件是否有訂閱者的標誌狀態,初始化為false*/
boolean subscriptionFound = false;
/*比如 A extends B implements C 釋出者post(A),那麼找訂閱者的時候不僅要找訂閱了事件A的訂閱者
* 還要找訂閱了B和C的訂閱者*/
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) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
/*如果設定了沒有訂閱者也照樣釋出事件並且該事件不是NoSubscriberEvent
或者SubscriberExceptionEvent型別
那麼就釋出一個特殊的事件型別*/
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
/**
* @param subscription
* @param event
* @param isMainThread
*/
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
/*在釋出者的執行緒裡邊通過反射執行事件處理方法*/
invokeSubscriber(subscription, event);
break;
case MainThread:
/*如果釋出者執行緒是主執行緒,直接在主執行緒裡邊通過反射執行事件處理方法*/
if (isMainThread) {
invokeSubscriber(subscription, event);
/*如果釋出者執行緒不是主執行緒,就把此事件加入主執行緒訊息迴圈處理佇列,在主執行緒中
* 通過反射呼叫事件處理方法*/
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
/*如果釋出者執行緒是主執行緒,那麼就把此事件加入後臺執行緒訊息迴圈處理佇列
* 通過反射呼叫事件處理方法,此執行緒模式多個事件都在一個後臺執行緒中迴圈處理
* 通過佇列管理多個事件*/
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);
}
}
四種執行緒模型之前已經分析過了。相關文章
- EventBus原始碼解讀詳細註釋(5)事件訊息繼承性分析 eventInheritance含義原始碼事件繼承
- EventBus原始碼解讀詳細註釋(1)register的幕後黑手原始碼
- EventBus原始碼解讀詳細註釋(2)MainThread執行緒模型分析原始碼AIthread執行緒模型
- EventBus原始碼解讀詳細註釋(4)register時重新整理的兩個map原始碼
- vue的事件冒泡 最詳細解釋版本Vue事件
- 從點選螢幕到事件處理的事件分發原始碼流程事件原始碼
- 設計模式之釋出訂閱模式(4) Guava Eventbus 事件處理設計模式Guava事件
- EventBus原始碼分析(三):post方法釋出事件【獲取事件的所有訂閱者,反射呼叫訂閱者事件處理方法】(2.4版本)原始碼事件反射
- Bootstrap的Model原始碼詳細註釋 (轉)boot原始碼
- 一文讀懂Guava EventBus(訂閱\釋出事件)Guava事件
- EventBus原始碼解讀詳細註釋(3)PostThread、MainThread、BackgroundThread、Async四種執行緒模式的區別原始碼threadAI執行緒模式
- .Net Core釋出到Linux下驗證碼失效處理方案詳解Linux
- JavaScript之事件處理詳解JavaScript事件
- Abp領域事件(EventBus)原始碼解析事件原始碼
- 「試著讀讀 Vue 原始碼」new Vue()發生了什麼 ❓Vue原始碼
- Promises A+規範原文解讀 + es6實現(附詳細註釋)Promise
- ViewGroup事件分發和處理原始碼分析View事件原始碼
- 最詳細的JavaScript和事件解讀JavaScript事件
- Android事件機制詳細解讀Android事件
- snabbdom原始碼解析(七) 事件處理原始碼事件
- 如何從MySQL中將變化的事件資料釋出到Kafka?MySql事件Kafka
- java事件處理模型是什麼Java事件模型
- Vue事件匯流排(EventBus)使用詳細介紹Vue事件
- ThinkPHP6 原始碼閱讀(十):事件PHP原始碼事件
- hover事件延遲處理程式碼例項詳解事件
- 解釋下什麼是事件代理?應用場景?事件
- 事件分發之View事件處理事件View
- EventBus 3.0+ 原始碼詳解(史上最詳細圖文講解)原始碼
- JavaScript 註冊事件處理函式JavaScript事件函式
- JavaScript註釋:單行註釋和多行註釋詳解JavaScript
- 【Django】runserver 0.0.0.0:0 後,究竟發生了什麼DjangoServer
- [webpack]原始碼解讀:命令列輸入webpack的時候都發生了什麼?Web原始碼命令列
- EventBus詳解及原始碼分析原始碼
- Nginx原始碼完全註釋(6)core/murmurhashNginx原始碼
- 從原始碼看 Android 事件分發原始碼Android事件
- 結合 AOP 輕鬆處理事件釋出處理日誌事件
- EventBus3.0解析之註解處理器S3
- Tarjan演算法及其應用 總結+詳細講解+詳細程式碼註釋演算法