從源碼的角度解析Android消息機(jī)制

1、概述

相信大家對Handler的使用再熟悉不過了,Handler也經(jīng)常被我們應(yīng)用于線程間通信。下面看一段很經(jīng)典的代碼:

 class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

當(dāng)我們在主線程或者在其它線程獲取到LooperThread線程中的mHandler實(shí)例并調(diào)用mHandler.sendMessage()時(shí),消息會(huì)傳遞到LooperThread線程中并在handleMessage方法中執(zhí)行對消息的特定處理。不知道大家有沒有想過為什么在一個(gè)線程發(fā)送消息另一個(gè)目標(biāo)線程能夠正確接收并做相應(yīng)處理?這篇文章就帶領(lǐng)大家從源碼的角度一步步理解Android的消息機(jī)制。Android的事件處理是基于消息循環(huán)的,Android的消息機(jī)制離不開Looper、MessageQueue、Handler,其實(shí)理解Android的消息機(jī)制就是理解以上三者的作用與聯(lián)系。

2、Looper

我們先來看看官方文檔對Looper的解釋

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler class.

從中我們可以知道,Looper是一個(gè)線程的消息循環(huán)器,線程默認(rèn)是沒有消息循環(huán)器的。我們可以通過prepare方法為一個(gè)線程創(chuàng)建消息循環(huán)器,然后通過調(diào)用loop方法處理消息直至循環(huán)終止。我們看看調(diào)用prepare方法后都發(fā)生了什么:

public final class Looper {
    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    private Printer mLogging;

     /** 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));
    }

在prepare方法中首先會(huì)通過調(diào)用ThreadLocal的get方法判斷當(dāng)前線程的消息循環(huán)器Looper是否為空,為空則通過Looper構(gòu)造器創(chuàng)建實(shí)例,否則拋出異常。從異常信息我們可以知道一個(gè)線程只有一個(gè)Looper。在這里ThreadLocal的作用是關(guān)聯(lián)線程和Looper,一個(gè)線程對應(yīng)一個(gè)Looper,即調(diào)用prepare方法的線程會(huì)和prepare方法創(chuàng)建的Looper實(shí)例關(guān)聯(lián),且一個(gè)線程和唯一一個(gè)Looper關(guān)聯(lián),當(dāng)?shù)诙握{(diào)用prepare方法時(shí)程序會(huì)拋出異常。接下來我們看看通過Looper構(gòu)造方法實(shí)例化對象時(shí)發(fā)生了什么:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

從源碼我們能夠看到實(shí)例化Looper時(shí),會(huì)初始化消息隊(duì)列MessageQueue(MessageQueue后續(xù)會(huì)詳解)以及記錄當(dāng)前Looper關(guān)聯(lián)的線程。MessageQueue作為Looper的成員變量在此時(shí)也與Looper建立了關(guān)聯(lián)。接下來我們看看Looper的另外一個(gè)方法loop:

 /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

從以上源碼我們不難理解,首先通過myLooper()方法獲取當(dāng)前線程的Looper實(shí)例,如果為空則拋出必須先調(diào)用prepare方法為當(dāng)前線程關(guān)聯(lián)Looper的異常信息。接下來進(jìn)入到loop方法中最重要的部分是一個(gè)for(;;)死循環(huán)。在循環(huán)中Looper關(guān)聯(lián)的消息隊(duì)列MessageQueue會(huì)通過調(diào)用next方法獲取消息隊(duì)列中的消息Message,并且循環(huán)會(huì)把消息分發(fā)給相應(yīng)的目標(biāo)對象。 msg.target.dispatchMessage(msg);這行代碼即是對消息的分發(fā)。其中target是Handler實(shí)例,即調(diào)用sendMessage方法發(fā)送的消息所關(guān)聯(lián)的Handler。Handler調(diào)用dispatchMessage分發(fā)消息最總會(huì)調(diào)用handleMessage方法這樣就進(jìn)入到了我們對消息的處理流程(后文會(huì)對Handler進(jìn)行詳解,這里提到只是為了便于大家理解)。我們看到Message msg = queue.next(); // might block這行源碼做了注釋,意思是next方法的調(diào)用可能會(huì)發(fā)生阻塞。也就是說當(dāng)消息隊(duì)列中有消息時(shí),next方法會(huì)返回消息對象否則當(dāng)前線程會(huì)一直阻塞直至隊(duì)列中有新消息把當(dāng)前線程喚醒。不知道大家注意到了loop方法的注釋沒有,Be sure to call * {@link #quit()} to end the loop.我們要調(diào)用quit方法結(jié)束循環(huán)。調(diào)用quit方法最終會(huì)使得MessageQueue next方法返回null,當(dāng)為空時(shí)會(huì)跳出for死循環(huán),這樣線程最終才可能結(jié)束生命。如果線程在處理完自己的任務(wù)后不調(diào)用quit方法,線程將一直阻塞或被新的無用消息喚醒而最終無法終止,這無疑是對資源的浪費(fèi)。
至此,Looper的源碼分析完了,當(dāng)然有一些方法并沒有分析(請大家自行閱讀),現(xiàn)在我們對Looper做一個(gè)總結(jié):一個(gè)線程唯一對應(yīng)一個(gè)Looper,一個(gè)Looper唯一關(guān)聯(lián)一個(gè)消息隊(duì)列MessageQueue。Looper不斷從消息隊(duì)列中取出消息并分發(fā)給目標(biāo)對象。還有要注意的是線程處理完相應(yīng)任務(wù)后要調(diào)用quit方法結(jié)束循環(huán),否則會(huì)造成不必要的資源浪費(fèi)。

2、MessageQueue

接下來我們繼續(xù)分析MessageQueue,首先我們還是看官方文檔的解釋

/**
 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 * 
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */

官方文檔解釋的很清楚,MessageQueue持有待分發(fā)的消息列表。我們通常不會(huì)直接操作MessageQueue插入消息而是通過Handler。我們可以通過Looper類的方法myQueue獲得當(dāng)前線程的MessageQueue。MessageQueue最重要的兩個(gè)作用是消息入隊(duì)和消息出隊(duì)。首先我們來看看入隊(duì)方法enqueueMessage的源碼

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            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;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

我們能夠看到前面幾行代碼對消息狀態(tài)進(jìn)行了判斷,包括如果消息分發(fā)的目標(biāo)對象為空或者消息已經(jīng)被使用將拋出異常,以及如果終止了消息循環(huán),消息將不會(huì)插入到隊(duì)列中并且返回false。接下來是消息入隊(duì)的核心代碼,可以看到是典型的鏈表結(jié)構(gòu):如果表頭為空則創(chuàng)建表頭,否則將數(shù)據(jù)插入到表的末尾。通過分析代碼我們也能夠知道,雖然MessageQueue字面意思是消息隊(duì)列,但它真正的內(nèi)部數(shù)據(jù)結(jié)構(gòu)并不是隊(duì)列而是普通鏈表。接下來我們繼續(xù)看出隊(duì)方法next的源碼,我們截取其中比較重要的一部分

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) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

從代碼中我們不難看出消息出隊(duì)其實(shí)就是鏈表表頭的向下移動(dòng),每次從消息隊(duì)列中取出一個(gè)消息,就返回表頭指向的Message實(shí)例,并且表頭向下移動(dòng)一位。上文我們提到了用Looper 的quit方法終止消息循環(huán),其實(shí)最終調(diào)用的是MessageQueue的quit方法。quit方法有個(gè)safe參數(shù),表示是否安全終止循環(huán)。所謂安全是當(dāng)調(diào)用quit方法后消息隊(duì)列中無法插入新的消息,但是循環(huán)可能不會(huì)立即終止,直至消息隊(duì)列中待分發(fā)的消息(如果有)分發(fā)完畢。不安全的終止與之相反,消息循環(huán)會(huì)立即終止,新的消息也無法插入。至此,MessageQueue的主要方法分析完畢了,其它的一些方法如removeMessage、quit等請大家自行閱讀分析,下面我們隊(duì)MessageQueue做一個(gè)總結(jié):MessagQueue的作用主要是存儲(chǔ)消息,并且對外提供一些接口對消息操作如插入消息、取出消息、移除消息等。

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

相關(guān)閱讀更多精彩內(nèi)容

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