Android Handler消息機(jī)制及消息類型(同步消息、異步消息、消息屏障)

Android 消息機(jī)制作為系統(tǒng)運(yùn)行的機(jī)制之一,在大一點(diǎn)的廠子面試被問到的概率比較大,可見它的重要性。下面將分兩部分,首先介紹消息的整體機(jī)制,接著聊聊消息的類型。

一、消息機(jī)制

在消息機(jī)制中,有下面幾個(gè)角色:

  • a. Message: 消息實(shí)體
  • b. MessageQueue: 消息隊(duì)列,存放Message,以鏈表的方式實(shí)現(xiàn)
  • c. Looper: 對(duì)MessageQueue進(jìn)行循環(huán),獲取Message給Handler處理
  • d. Handler: 對(duì)Message進(jìn)行處理
    下面從源碼的角度分析它們之間是怎么協(xié)作的
Looper:
public final class Looper {
    ...

    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    ...
}

從上述源碼可知,Looper提供了兩個(gè)方法來創(chuàng)建Looper對(duì)象,并將創(chuàng)建的對(duì)象保存在sThreadLocal。
從prepare方法可以看到,每個(gè)線程只允許創(chuàng)建一個(gè)Looper對(duì)象,否則會(huì)拋異常。
而我們?cè)谥骶€程創(chuàng)建Handler時(shí),則不用自己創(chuàng)建Looper,那主線程的Looper是在哪里被創(chuàng)建的呢?我們看下prepareMainLooper()方法的注釋
在 Looper 的 prepareMainLooper() 方法注釋中可以看到這樣一句話:

Initialize the current thread as a looper, marking it as an application's main looper.
The main looper for your application is created by the Android environment, so 
you should never need to call this function yourself.  See also: {@link #prepare()}

意思是說:將當(dāng)前線程初始化為looper,將其標(biāo)記為應(yīng)用程序的主循環(huán)。您的應(yīng)用程序的主要循環(huán)器是由Android環(huán)境創(chuàng)建的,永遠(yuǎn)不應(yīng)該自己調(diào)用這個(gè)函數(shù)。
由此可知,是系統(tǒng)運(yùn)行時(shí)就幫我們創(chuàng)建了Looper了,可以看下ActivityThread的main方法:

public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ...
    Looper.loop();
}

印證了上述的注釋。
接下來看下里面loop()方法:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        ...
        for (;;) {
            // 從消息隊(duì)列中取得消息,具體的在下面MessageQueue進(jìn)行解析
            Message msg = queue.next(); 
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
            try {
                // target是Handler,由Handler進(jìn)行處理,具體看下面Handler的解析
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            ...
        }
    }
Handler:

首先是send系列方法,拿一個(gè)出來看:

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

可見最后是調(diào)用了sendMessageAtTime方法,其它的方法包括post系列的方法也一樣,最終都是調(diào)用了sendMessageAtTime,不同的是最后傳入的uptimeMillis。
然后再看下Looper調(diào)用Handler的dispatchMessage方法

    /**
     * Handle system messages here.
     * 處理消息
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
MessageQueue:

接下來看下sendMessageAtTime里的enqueueMessage方法,此方法為將一個(gè)消息入隊(duì)。進(jìn)入MessageQueue類

    // msg-上面說的消息實(shí)體, when-需要延時(shí)執(zhí)行的時(shí)長(zhǎng)
    boolean enqueueMessage(Message msg, long when) {
        // 參數(shù)檢查省略
        synchronized (this) {
            // 檢查L(zhǎng)ooper及相應(yīng)的隊(duì)列是否已經(jīng)終止,是的話對(duì)msg進(jìn)行回收并退出
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;  // mMessages指向隊(duì)列頭部
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                // 假設(shè)隊(duì)列為空 或 msg不需要延時(shí) 或 
                // msg需要延時(shí)的時(shí)長(zhǎng)小于隊(duì)頭消息的延時(shí)時(shí)長(zhǎng),
                // 則將msg插入對(duì)頭
                msg.next = p;
                mMessages = msg;
                // 標(biāo)記是否需要喚醒,mBlocked表示現(xiàn)在消息隊(duì)列是否處于阻塞狀態(tài)
                // 阻塞的原因在下面next()方法獲取消息時(shí)再進(jìn)行說明
                needWake = mBlocked; 
            } else {
                // 將msg插入在隊(duì)列的中部,插入的位置
                // 根據(jù)when得到,when小的msg會(huì)在前面
                // 這樣方便保證之后先出隊(duì)msg都是需要
                // 先執(zhí)行的,從而保證在delay的時(shí)候過后執(zhí)行msg
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            // 當(dāng)在next()獲取msg時(shí),假設(shè)獲取到的when大于0,
            // 說明此時(shí)需要延時(shí)when后才能
            // 執(zhí)行這個(gè)msg,因此進(jìn)行了阻塞,mBlocked=true。
            // 但是這個(gè)時(shí)候有新的消息入隊(duì)并處于隊(duì)頭位置,
            // 因此先于上一個(gè)隊(duì)頭消息執(zhí)行,所醒
            // 以此時(shí)需要喚醒隊(duì)列,才能保證后來者需要先執(zhí)行
            // 的不會(huì)因?yàn)樽枞袛?            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

接下來看看上面提到的出隊(duì)的方法next():

    Message next() {
        // 條件檢查,省略
        // 第一次循環(huán)的時(shí)候?yàn)?1,其它情況不會(huì)時(shí)-1
        int pendingIdleHandlerCount = -1; 
        int nextPollTimeoutMillis = 0; // 需要進(jìn)行阻塞的時(shí)間
        for (;;) { // 死循環(huán),不斷獲取msg
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // 當(dāng)頭部消息還未到執(zhí)行時(shí)間時(shí),
            // 調(diào)用本地方法進(jìn)行阻塞掛起
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) { // 判斷是否有消息屏障
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    // 有消息屏障的話,取出后面第一條異步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // 下一個(gè)消息還未到執(zhí)行時(shí)間,因此設(shè)置一個(gè)時(shí)間進(jìn)行阻塞,
                        // 過了這個(gè)時(shí)間,將喚醒
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 此時(shí)有消息到執(zhí)行時(shí)間了,則設(shè)置隊(duì)列處于不阻塞狀態(tài),
                        // 將隊(duì)頭出隊(duì),并返回
                        mBlocked = false; 
                        if (prevMsg != null) { // 有消息屏障的情況下,將鏈表的前部分的同步消息連接到后面
                            prevMsg.next = msg.next;
                        } else { // 否則,直接將mMessages指向下一個(gè)消息即可
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 如果沒有消息,則設(shè)置阻塞時(shí)長(zhǎng)為無限,直到被喚醒
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // 第一次循環(huán) 且 (消息隊(duì)列為空 或 
                // 消息隊(duì)列的第一個(gè)消息的觸發(fā)時(shí)間還沒有到)時(shí),
                // 表示處于空閑狀態(tài)
                // 獲取到 IdleHandler 數(shù)量
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // 沒有 IdleHandler 需要運(yùn)行,循環(huán)并等待
                    mBlocked = true; // 設(shè)置阻塞狀態(tài)為 true
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // 運(yùn)行 IdleHandler,只有第一次循環(huán)時(shí)才會(huì)運(yùn)行
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // 重置 IdleHandler 的數(shù)量為 0,確保不會(huì)重復(fù)運(yùn)行
            // pendingIdleHandlerCount 置為 0 后,上面可以通
            // 過 pendingIdleHandlerCount < 0 判斷是否是第一次循環(huán),
            // 不是第一次循環(huán)則 pendingIdleHandlerCount 的值不會(huì)變,始終為 0。
            pendingIdleHandlerCount = 0;

            // 在執(zhí)行 IdleHandler 后,可能有新的消息插
            // 入或消息隊(duì)列中的消息到了觸發(fā)時(shí)間,
            // 所以將 nextPollTimeoutMillis 置為 0,表示不
            // 需要阻塞,重新檢查消息隊(duì)列。
            nextPollTimeoutMillis = 0;
        }
    }
經(jīng)過上面兩個(gè)方法,這里總結(jié)下消息機(jī)制的總體流程:

消息在入隊(duì)(即有新消息加入)的時(shí)候,會(huì)根據(jù)delay的時(shí)間,在隊(duì)列找到合適的位置入隊(duì),從而保證整個(gè)隊(duì)列的順序是以延遲時(shí)間從小到大排序。

  • a. 當(dāng)入隊(duì)的消息的delay時(shí)間比原先隊(duì)頭消息短的時(shí)候,或者隊(duì)列為空的時(shí)候,則消息會(huì)入隊(duì)在隊(duì)頭,并且當(dāng)此時(shí)列表處于阻塞狀態(tài)時(shí),則喚醒隊(duì)列;
  • b. 否則入隊(duì)的位置為非隊(duì)頭。

這個(gè)特性方便后續(xù)獲取消息即出隊(duì)的時(shí)候,直接出隊(duì)頭消息,即是最優(yōu)先需要執(zhí)行的消息。出隊(duì)時(shí)

  • a. 若隊(duì)列為空,則無限長(zhǎng)時(shí)間進(jìn)行阻塞;
  • b. 出隊(duì)的消息要是到達(dá)執(zhí)行時(shí)間了,則出隊(duì);
  • c. 出隊(duì)的消息還沒到執(zhí)行時(shí)間,則進(jìn)行對(duì)應(yīng)時(shí)間的阻塞。

二、消息類型(同步消息、異步消息、消息屏障)

上面已經(jīng)介紹完了Handler的機(jī)制,代碼注釋中提及了消息屏障、異步消息,下面具體介紹下這部分內(nèi)容。

Hander的消息類型有三種,分別是同步消息、異步消息、消息屏障,下面先看看使用方式。

1. 同步消息

我們平常使用Handler發(fā)送的消息基本都是同步消息,例如下面的代碼:

        Handler handler = new Handler(Looper.getMainLooper());

        // 使用方式1
        handler.post(new Runnable() {
            @Override
            public void run() {
                // do something
            }
        });

        // 使用方式2
        handler.sendEmptyMessage(0);

        // 使用方式3
        Message msg = Message.obtain();
        msg.what = 1;
        handler.sendMessage(msg);

        ...
2. 異步消息

使用方式如下:

        Message msg = Message.obtain();
        msg.what = 2;
        msg.setAsynchronous(true); // 設(shè)置消息為異步消息
        handler.sendMessage(msg);
3. 消息屏障

添加消息屏障的API如下:

    /**
     * Posts a synchronization barrier to the Looper's message queue.
     *
     * @return A token that uniquely identifies the barrier.  This token must be
     * @hide
     */
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

該方法是@hide的,因此需要自行反射調(diào)用,添加完消息屏障后,會(huì)返回這個(gè)消息的token,移除消息屏障時(shí),需要用到這個(gè)token,具體API如下(同樣需要反射調(diào)用):

    /**
     * Removes a synchronization barrier.
     *
     * @param token The synchronization barrier token that was returned by
     * {@link #postSyncBarrier}.
     *
     * @throws IllegalStateException if the barrier was not found.
     *
     * @hide
     */
    public void removeSyncBarrier(int token) {
    }

上面介紹了三種消息類型的大體使用方式,那它們有什么作用,或者有什么區(qū)別呢?

下面回到MessageQueue#next()的源碼中:

    Message next() {
        for (;;) {
            synchronized (this) {
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) { // 判斷是否有消息屏障
                    // 有消息屏障的話,取出后面第一條異步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                    } else {
                        if (prevMsg != null) { // 有消息屏障的情況下,將鏈表的前部分的同步消息連接到后面
                            prevMsg.next = msg.next;
                        } else { // 否則,直接將mMessages指向下一個(gè)消息即可
                            mMessages = msg.next;
                        }
                    }
                }
            }
        }
    }

從上面的代碼可以看出,消息屏障的作用是來阻塞消息隊(duì)列后面的同步消息,而異步消息不受消息屏障影響。在無消息屏障的情況下,同步消息與異步消息無本質(zhì)上的區(qū)別。

轉(zhuǎn)載請(qǐng)注明出處:http://www.itdecent.cn/p/9ecccd6d4506

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容