EventBus原始碼解讀詳細註釋(2)MainThread執行緒模型分析

王世暉發表於2016-03-20

[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的執行緒模型

為何要定義執行緒模型?因為在Android的中執行緒的使用有以下的限制:
1 主執行緒不能被阻塞,UI的更新位於主執行緒,耗時操作如網路處理在後臺執行緒 
2 事件的傳送和處理可能會位於不同執行緒
通過使用EvenBus的執行緒模型,我們可以定義處理事件的執行緒模型。EventBus中有四種執行緒模型:PostThread,MainThread,BackgroundThread,Async,下面分別介紹之。 
PostThread 
預設的執行緒模型,事件釋出和接收在相同的執行緒,適合用於完成時間非常短的任務,以免阻塞UI。例:
// Called in the same thread (default)
public void onEvent(MessageEvent event) {
    log(event.message);
}
MainThread 
訂閱者的事件處理方法在主執行緒被呼叫,適合處理開銷小的事件,以免阻塞主執行緒。例:
// Called in Android UI's main thread
public void onEventMainThread(MessageEvent event) {
    textField.setText(event.message);
}
BackgroundThread 
訂閱者的事件處理在後臺被呼叫。若事件傳送執行緒位於主執行緒,EventBus將使用一個後臺執行緒來逐個傳送事件
// Called in the background thread
public void onEventBackgroundThread(MessageEvent event){
    saveToDisk(event.message);
}
Async 
事件處理位於一個單獨的執行緒,該執行緒往往既不是傳送事件的執行緒,也不是主執行緒。使用這個模型適用於事件傳送無需等待事件處理後才返回,適合處理耗時操作,如請求網路。EventBus內部使用執行緒池來管理多個執行緒。例:
// Called in a separate thread
public void onEventAsync(MessageEvent event){
    backend.send(event.message);
}

接下來從原始碼的角度分析MainThread 

EventBus有個私有方法postToSubscription,這個私有方法是如何呼叫的且聽下回分解,先不管
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;
通過解析執行緒模型,如果是MainThread執行緒模型,並且當前執行緒就是主執行緒,那麼可以直接在當前主執行緒通過反射呼叫訂閱者的事件處理方法;
如果當前執行緒不是主執行緒呢?當然只能藉助Handler了,將此事件傳送到主執行緒處理。
mainThreadPoster是EventBus的一個屬性,型別為HandlerPoster
是這樣構造的
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
傳入的第二個引數是主執行緒的Looper,第三個參數列示主執行緒事件處理最大時間為10ms,超時將重新排程事件
下邊看下HandlerPoster的內部是幹什麼的 
final class HandlerPoster extends Handler {
    private final PendingPostQueue queue;
    /*事件處理最大時間,超時重新排程*/
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }
    /*入佇列操作,傳入訂閱者和事件*/
    void enqueue(Subscription subscription, Object event) {
        /*將訂閱者和事件封裝成PendingPost*/
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                /**   public final boolean sendMessage(Message msg)
                 * Pushes a message onto the end of the message queue after all pending messages
                 * before the current time. It will be received in handleMessage,
                 * in the thread attached to this handler.
                 * @return Returns true if the message was successfully placed in to the
                 *         message queue.  Returns false on failure, usually because the
                 *         looper processing the message queue is exiting.
                 */
                /**  public final Message obtainMessage()
                 * Returns a new  android.os.Message Messagefrom the global message pool. More efficient than
                 * creating and allocating new instances. The retrieved message has its handler set to this instance
                 * (Message.target == this).
                 */

                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            /**  native public static long uptimeMillis();
             * Returns milliseconds since boot, not counting time spent in deep sleep.
             * @return milliseconds of non-sleep uptime since boot.
             */
            /*記錄迴圈開始的時間*/
            long started = SystemClock.uptimeMillis();
            /*死迴圈,從訊息佇列取出訊息逐一處理*/
            while (true) {
                /*獲取隊首*/
                PendingPost pendingPost = queue.poll();
                /*如果佇列為空*/
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            /*如果隊裡中沒有訊息需要處理,設定標誌位,然後直接返回,退出迴圈*/
                            handlerActive = false;
                            return;
                        }
                    }
                }
                /*運用反射呼叫事件處理方法*/
                eventBus.invokeSubscriber(pendingPost);
                /*計算迴圈所用的時間*/
                long timeInMethod = SystemClock.uptimeMillis() - started;
                /*如果事件處理時間太長,超時,那麼就重新排程*/
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}
這個類主要實現了一個在UI執行緒也就是主執行緒迴圈處理事件的功能,同時如果事件處理超時,將被重新排程。
順便看下EventBus的資料結構是如何設計的
先看訊息佇列:
final class PendingPostQueue {
    private PendingPost head;
    private PendingPost tail;
    /*入佇列操作*/
    synchronized void enqueue(PendingPost pendingPost) {
        /*良好程式設計習慣,入口引數檢查*/
        if (pendingPost == null) {
            throw new NullPointerException("null cannot be enqueued");
        }
        /*尾部不是空指標,佇列有尾部,後邊加一個PendingPost資料,該資料指標域已初始化為空指標*/
        if (tail != null) {
            tail.next = pendingPost;
            tail = pendingPost;
            /*佇列頭部為空指標,那麼肯定沒有頭部,也沒有尾部,佇列為空,把入隊的新元素作為頭部和尾部*/
        } else if (head == null) {
            head = tail = pendingPost;
            /*佇列有頭無尾丟擲異常*/
        } else {
            throw new IllegalStateException("Head present, but no tail");
        }
        /**
         * Causes all threads which are waiting on this object's monitor (by means
         * of calling one of the code wait() methods) to be woken up. The threads
         * will not run immediately. The thread that called code notify() has to
         * release the object's monitor first. Also, the threads still have to
         * compete against other threads that try to synchronize on the same object.
         *This method can only be invoked by a thread which owns this object's
         * monitor. A thread becomes owner of an object's monitor
         * by executing a synchronized method of that object;
         * by executing the body of a code synchronized statement that synchronizes on the object;
         * by executing a synchronized static method if the object is of type code Class
         *  1)wait()、notify()和notifyAll()方法是本地方法,並且為final方法,無法被重寫。
           2)呼叫某個物件的wait()方法能讓當前執行緒阻塞,並且當前執行緒必須擁有此物件的monitor(即鎖)
           3)呼叫某個物件的notify()方法能夠喚醒一個正在等待這個物件的monitor的執行緒,
                如果有多個執行緒都在等待這個物件的monitor,則只能喚醒其中一個執行緒;
           4)呼叫notifyAll()方法能夠喚醒所有正在等待這個物件的monitor的執行緒;

            呼叫某個物件的wait()方法,當前執行緒必須擁有這個物件的monitor(即鎖),
            因此呼叫wait()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法)
            呼叫某個物件的wait()方法,相當於讓當前執行緒交出此物件的monitor,然後進入等待狀態,
            等待後續再次獲得此物件的鎖(Thread類中的sleep方法使當前執行緒暫停執行一段時間,從而讓其他執行緒有機會繼續執行,但它並不釋放物件鎖);
           notify()方法能夠喚醒一個正在等待該物件的monitor的執行緒,當有多個執行緒都在等待該物件的monitor的話,
            則只能喚醒其中一個執行緒,具體喚醒哪個執行緒則不得而知。
           同樣地,呼叫某個物件的notify()方法,當前執行緒也必須擁有這個物件的monitor,
            因此呼叫notify()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法)。
           nofityAll()方法能夠喚醒所有正在等待該物件的monitor的執行緒,這一點與notify()方法是不同的。
           這裡要注意一點:notify()和notifyAll()方法只是喚醒等待該物件的monitor的執行緒,並不決定哪個執行緒能夠獲取到monitor。
            一個執行緒被喚醒不代表立即獲取了物件的monitor,只有等呼叫完notify()或者notifyAll()並退出synchronized塊,
            釋放物件鎖後,其餘執行緒才可獲得鎖執行。
         */
        notifyAll();
    }
    /*隊首出佇列*/
    synchronized PendingPost poll() {
        PendingPost pendingPost = head;
        if (head != null) {
            /*隊首出佇列,將原佇列第二個資料作為新的隊首*/
            /*注意原佇列只有一個資料的情況,隊首==隊尾都不是null,現在若隊首出佇列,隊尾應該設定null*/
            head = head.next;
            if (head == null) {
                tail = null;
            }
        }
        /*佇列為空,返回空指標*/
        return pendingPost;
    }
    /*如果佇列為空,掛起當前執行緒,知道有新的資料加入佇列(入佇列操作後呼叫了notifyall() )*/
    synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
        if (head == null) {
            /**
             * Causes the calling thread to wait until another thread calls the
             * notify() or  notifyAll()method of this object or until the
             * specified timeout expires. This method can only be invoked by a thread
             * which owns this object's monitor;
             * While the thread waits, it gives up ownership of this object's
             * monitor. When it is notified (or interrupted), it re-acquires the monitor
             * before it starts running.
             * A timeout of zero means the calling thread should wait forever unless interrupted or
             * notified.
             * wait():
             等待物件的同步鎖,需要獲得該物件的同步鎖才可以呼叫這個方法,
             否則編譯可以通過,但執行時會收到一個異常:IllegalMonitorStateException。
             呼叫任意物件的 wait() 方法導致該執行緒阻塞,該執行緒不可繼續執行,並且該物件上的鎖被釋放。
             notify():
             喚醒在等待該物件同步鎖的執行緒(只喚醒一個,如果有多個在等待),注意的是在呼叫此方法的時候,並不能確切的喚醒某一個等待狀態的執行緒,
             而是由JVM確定喚醒哪個執行緒,而且不是按優先順序。
             呼叫任意物件的notify()方法則導致因呼叫該物件的 wait()方法而阻塞的執行緒中隨機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。
             notifyAll():
             喚醒所有等待的執行緒,注意喚醒的是notify之前wait的執行緒,對於notify之後的wait執行緒是沒有效果的。
             */
            wait(maxMillisToWait);
        }
        /*獲得物件鎖後返回隊首資料*/
        return poll();
    }
}

再看訊息類:
final class PendingPost {
    /*開一個物件池,靜態ArrayList實現*/
    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
    /*資料部分是事件和訂閱者*/
    Object event;
    Subscription subscription;
    /*保持連結串列結構,指向下一個節點*/
    PendingPost next;
    /*私有建構函式,封裝事件和訂閱者*/
    private PendingPost(Object event, Subscription subscription) {
        this.event = event;
        this.subscription = subscription;
    }
    /*傳入事件和訂閱者,返回一個封裝好的PendingPost*/
    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        /*對物件池加鎖*/
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            /*如果物件池儲存的有備胎物件,就用備胎物件,節省一次new物件*/
            if (size > 0) {
                /*從池子裡邊拿出來一個PendingPost*/
                /*   * Removes the object at the specified location from this code List.
                     * @param location   the index of the object to remove.
                     * @return the removed object.
                     * 從池子也就是list拿出來一個PendingPost*/
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        /*物件池沒有儲存備胎物件,就只能新建立一個物件了*/
        return new PendingPost(event, subscription);
    }
    /*歸還一個物件給物件池*/
    static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost);
            }
        }
    }
}




相關文章