EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!

王世暉發表於2016-03-21

[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);
        }
    }
四種執行緒模型之前已經分析過了。






相關文章