1. 程式人生 > >Android訊息機制分析:Handler、Looper、MessageQueue原始碼分析

Android訊息機制分析:Handler、Looper、MessageQueue原始碼分析

1.前言

關於Handler訊息機制的部落格實際上是非常多的了。
之前也是看別人的部落格過來的,但是過了一段時間之後,一些細節也就忘了。
所以,就自己擼一篇,權當筆記,方便以後翻閱。

這篇文章主要是分析Handler訊息機制原理以及收集一些面試題來講解,熟悉的話可以不用看了。

本文原始碼基於android 27

2.Android訊息機制概述

2.1 本質

Android的訊息機制本質就是一套訊息傳送,傳遞及處理的機制。

2.2 角色說明

角色 作用
Handler
(處理者)
負責訊息的傳送及處理等等。
Message
(訊息)
儲存訊息的內容,如儲存一個訊息型別(what)等等。
MessageQueue
(訊息佇列)
儲存Message的資料結構,是一個連結串列。
Looper
(迴圈器)
從訊息佇列中迴圈的取出訊息然後把訊息交給Handler處理。

2.3 原理簡介

訊息機制在Android上的流程為:

  1. 應用啟動時會在主執行緒中建立一個Looper(迴圈器),Looper(迴圈器)內部則會建立一個MessageQueue(訊息佇列);
  2. 然後Looper(迴圈器)就會不斷的輪詢MessageQueue(訊息佇列)中是否有Message(訊息);
  3. 我們可以通過Handler
    去傳送一個Message(訊息),傳送之後Message(訊息)就會進入到MessageQueue(訊息佇列)中去,Looper(迴圈器)通過輪詢取出Message(訊息),然後交給相應的Handler(處理者)去處理。

下面會通過分析原始碼來驗證這個過程。

上面這個流程總結成一句話就是:

Looper(迴圈器)通過不斷的從MessageQueue(訊息佇列)中取出Message(訊息),然後交給相應的Handler(處理者)去處理。

是不是很簡單呢?

2.4 Handler的應用–UI更新

對於訊息機制,我們平常接觸的最多的場景就是:

在子執行緒中進行一些資料更新等耗時操作,然後使用Handler

在主執行緒中去更新UI。

為什麼要怎麼操作呢?這裡有兩個前提:

1.Android開發中規定了UI的更新只能在主執行緒中去操作,在子執行緒中更新會報錯。
2.我們在主執行緒中建立的Handler能夠接受到同一個Handler在子執行緒中傳送的訊息。

可以看到,在這種場景下我們使用Handler的目的就是切換到主執行緒中去更新UI。而Handler的使用方式是很簡單的,這裡就不寫例子了。

那麼,為什麼更新UI只能在主執行緒中去操作呢?

這是因為Android中的UI控制元件不是執行緒安全的,因此在多執行緒中併發,可能會出現執行緒安全的問題,即訪問UI控制元件可能會出現跟預期不一樣的結果。那麼為什麼不使用鎖機制呢?因為加鎖會降低訪問UI的效率,鎖機制會阻塞某些執行緒的執行。因此,最簡單高效的方法就是使用單執行緒模型來進行UI的訪問了。

那麼,為什麼主執行緒中的Handler能接受到其他執行緒發來的訊息呢?
這是後面原始碼分析的內容,這裡暫且不表。

2.5 Handler的其他應用

上面UI更新實際上只是訊息機制其中一個應用場景。
如果我們瞭解四大元件的啟動停止等過程的話,就會發現,都是在一個名為HHandler中處理狀態切換等邏輯,這個HActivityThread的內部類。其本質就是切到主執行緒中去處理。

所以說,不要將Handler僅僅侷限於UI更新。

3.原始碼分析

本節主要深入原始碼對訊息機制進行分析。對涉及到LooperMessageQueueMessageHandler等類進行逐一分析。

3.1 Looper類

3.1.1 Looper(迴圈器)的建立

Looper的建立可以分為在主執行緒中建立以及在子執行緒中建立,我們分別來看下。

3.1.1.1 主執行緒中建立Looper

先來看下Looper在主執行緒中是什麼時候建立的。

3.1.1.1.1 ActivityThread的main()

應用啟動時,會呼叫到ActivityThread中的main()方法,這個main()方法是應用程式的入口。main()裡面會建立一個Looper物件出來。我們來看下程式碼:

    public static void main(String[] args) {
        //省略無關程式碼

        //為主執行緒建立1個Looper物件
        Looper.prepareMainLooper();

        //建立主執行緒
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        //省略無關程式碼

        //開啟訊息迴圈
        Looper.loop();
    }

可以看到,應用啟動時就為主執行緒創建出一個Looper物件,並且開啟訊息迴圈。

3.1.1.1.2 Looper的prepareMainLooper()

再來看下prepareMainLooper()

    public static void prepareMainLooper() {
        //最終還是呼叫prepare()
        //引數false表示主執行緒中的訊息迴圈不允許退出
        prepare(false);

        //判斷sMainLooper是否為null,否則丟擲異常
        //即prepareMainLooper()不能夠被呼叫兩次
        //保證了主執行緒中只存在一個Looper物件
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
3.1.1.1.3 Looper的prepare()

再來看下prepare()方法:

    private static void prepare(boolean quitAllowed) {
        //ThreadLocal可以儲存一個執行緒內的區域性變數
        //這裡判斷當前執行緒是否已經存在Looper物件,存在的話則拋異常
        //因為一個執行緒只能建立一個Looper物件
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //當前執行緒沒有建立Looper物件的話
        //則新建立一個Looper物件
        //並把這個Looper物件儲存到ThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }

prepare()中就是建立一個Looper物件並把Looper物件儲存到執行緒中的ThreadLocal

3.1.1.1.4 Looper的構造方法

再來看下Looper的構造方法:

    private Looper(boolean quitAllowed) {
        //建立訊息佇列
        mQueue = new MessageQueue(quitAllowed);
        //記錄當前執行緒.
        mThread = Thread.currentThread();
    }

Looper內部中就是建立了一個訊息佇列。

3.1.1.1.5 小結

應用啟動時,主執行緒會建立一個Looper物件出來,Looper內部則建立訊息佇列。

3.1.1.2 子執行緒中建立Looper

在子執行緒中建立Looper非常簡單,直接看例子吧:

3.1.1.2.1 子執行緒中建立Looper例子
class LooperThread extends Thread {
    public Handler mHandler;

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

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                //TODO ...
            }
        };

        Looper.loop(); 
    }

呼叫一下無引數的prepare()方法即可。

3.1.1.2.2 Looper的prepare()
    public static void prepare() {
        prepare(true);
    }

最終還是呼叫有引數的prepare()方法,有引數的prepare()方法祥見上面的程式碼分析。
true代表這個Looper是可以退出的。主執行緒中建立的Looper則是不能退出的。這就是他們的區別。

3.1.2 Looper(迴圈器)的訊息迴圈

Looper是通過loop()這個方法來進行訊息迴圈的,我們來看下程式碼:

3.1.2.1 Looper的loop()
    public static void loop() {
        //獲得當前執行緒的Looper物件
        //myLooper()實際上通過sThreadLocal.get()來獲取的
        final Looper me = myLooper();
        //如果當前執行緒沒有建立過Looper,則丟擲異常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //獲得Looper物件中的訊息佇列
        final MessageQueue queue = me.mQueue;

        //死迴圈
        for (;;) {
            //從訊息佇列中取出一個訊息
            //如果訊息佇列沒有訊息的話,會阻塞在這裡
            Message msg = queue.next(); // might block
            //訊息為null的話表示停止訊息迴圈
            //可以通過queue.quit()來停止,前提是通過prepare(true);來建立的
            //主執行緒中不允許停止訊息迴圈
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //...

            //分發訊息去處理
            //msg.target就是要處理的Handler,祥見後面分析
            msg.target.dispatchMessage(msg);

            //...

            //回收訊息
            msg.recycleUnchecked();
        }
    }
3.1.2.2 小結

Looper中通過一個死迴圈從訊息佇列中取訊息,一旦取到訊息之後,就分發交給Handler來處理。

3.1.3 Looper(迴圈器)的退出

    public void quit() {
        mQueue.quit(false);
    }

    //安全退出
    public void quitSafely() {
        mQueue.quit(true);
    }

退出Looper有兩個方法,如上。最終還是通過呼叫MessageQueue.quit(boolean safe)方法來實現,只是傳的引數不一樣而已。這個在MessageQueue那一小節再來分析。

3.2 Message類

Message類用來儲存訊息的內容。我們先來看下Message類會儲存哪些訊息:

3.2.1 Message類主要成員變數

成員變數 型別 含義
what int 訊息型別
obj Object 訊息內容
when long 訊息觸發時間
target Handler 訊息處理者
callback Runnable 回撥方法
sPool Message 訊息池
sPoolSize int 訊息池大小
next Message 下一條訊息

這裡只列舉了一部分,詳細的可以去看Message類的原始碼。

3.2.2 獲取訊息

Message內部維護了一個訊息池,我們可以通過obtain()來從訊息池中獲取訊息,而不是直接去new,這樣可以提高效率。

3.2.2.1 Message的obtain()
    public static Message obtain() {
        synchronized (sPoolSync) {
            //如果訊息池不為null,則從訊息池中取出一條訊息
            if (sPool != null) {
                //從sPool中取出頭結點
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // 清除in-use標記
                sPoolSize--;
                return m;
            }
        }
        //如果訊息池為null,則直接new
        return new Message();
    }

3.2.3 回收訊息

3.2.3.1 Message的recycle()
    public void recycle() {
        //判斷訊息是否正在使用
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        //呼叫recycleUnchecked()
        recycleUnchecked();
    }

呼叫recycleUnchecked()來回收。

3.2.3.2 Message的recycleUnchecked()
    void recycleUnchecked() {
        //將訊息標記為使用狀態
        //清空訊息其他引數
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            //如果沒有達到訊息池的最大容量,則將訊息回收到訊息池中去
            //最大容量預設為50
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

3.2.4 小結

Message內部維護了一個訊息池,這個訊息池是以連結串列的形式存在的。通過訊息池去獲取Message物件能夠避免直接建立物件,可以起到一個提高效率的作用。

3.3 Handler類

通常,我們都會通過繼承Handler類來自定義一個Handler子類,然後重寫handleMessage()方法來處理訊息。同時,也是通過這個Handler子類來進行傳送訊息的。

我們先來看下Handler的構造方法。

3.3.1 Handler的構造方法

3.3.1.1 Handler的無參構造方法
    public Handler() {
        this(null, false);
    }

最終會呼叫有引數的構造方法Handler(Callback callback, boolean async)

3.3.1.2 Handler的有參構造方法

先來看下上面提到的Handler(Callback callback, boolean async)

    public Handler(Callback callback, boolean async) {
        //匿名類、內部類或本地類應該申明為static,否則會警告可能出現記憶體洩露
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        //獲得當前執行緒的Looper物件
        //myLooper()實際上通過sThreadLocal.get()來獲取的
        mLooper = Looper.myLooper();
        //如果mLooper為null則丟擲異常
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

        //獲得Looper物件中的訊息佇列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

下面列出所有Handler的所有有參構造方法

    public Handler(Callback callback);
    public Handler(Looper looper);
    public Handler(Looper looper, Callback callback);
    public Handler(boolean async);
    public Handler(Callback callback, boolean async);
    public Handler(Looper looper, Callback callback, boolean async);

可以看到:除了callbackasync之外,還可以傳遞一個Looper進來,即可以指定跟Handler要繫結的Looper,相關程式碼就不貼了,還是很簡單的。

3.3.2 Handler傳送訊息

通常我們都是通過HandlersendMessage()方法來發送訊息的:

3.3.2.1 Handler的sendMessage()
    public final boolean sendMessage(Message msg)
    {   
        //呼叫sendMessageDelayed()
        return sendMessageDelayed(msg, 0);
    }
3.3.2.2 Handler的sendMessageDelayed()
   public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //呼叫sendMessageDelayed()
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
3.3.2.3 Handler的sendMessageAtTime()
    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;
        }
        //呼叫enqueueMessage(),訊息入隊
        return enqueueMessage(queue, msg, uptimeMillis);
    }
3.3.2.4 Handler的enqueueMessage()
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //把當前的Handler物件儲存到訊息中的target中去
        //這樣訊息分發時才能找到相應的Handler去處理
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //把訊息放到訊息佇列中去
        return queue.enqueueMessage(msg, uptimeMillis);
    }

最終,傳送訊息就是將訊息放到訊息佇列中去。

3.3.3 Handler傳送Runnable

Handler除了sendMessage(Message msg)外,還可以傳送一個Runnable出來,這是通過其post()方法實現的:

3.3.3.1 Handler的post()
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

post()方法裡面同樣也是通過sendMessageDelayed()來發送訊息,我們來看下getPostMessage(r)這個方法:

3.3.3.1 Handler的getPostMessage()
    private static Message getPostMessage(Runnable r) {
        //獲取訊息
        Message m = Message.obtain();
        //Runnable賦值給Message中的callback
        m.callback = r;
        //返回一個Message
        return m;
    }

所以,post()最終還是將Runnable物件包裝成一個Message來進行訊息傳送的。

3.3.4 分發訊息

前面Looper在訊息佇列中取到訊息後就呼叫msg.target.dispatchMessage(msg);來分發訊息,這裡的msg.target就是Handler。我們來看下dispatchMessage()方法:

3.3.4.1 Handler的dispatchMessagee()
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //當msg.callback不為null時,會回撥message.callback.run()方法
            //即執行Runnable的run()方法
            handleCallback(msg);
        } else {
            //當Handler存在Callback時,回撥Callback的handleMessage();
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //呼叫Handler的handleMessage(msg)
            //即我們繼承Handler重寫的handleMessage(msg)方法
            handleMessage(msg);
        }
    }

我們再來看下handleCallback()這個方法:

3.3.4.2 Handler的handleCallback()
    private static void handleCallback(Message message) {
        //執行Runnable的run()方法
        message.callback.run();
    }
3.3.4.3 小結

從上面的程式碼可以看到,分發訊息的流程如下:

  1. 如果msg.callback不為null,這個msg.callback實際是個Runnable物件,則呼叫這個Runnablerun()方法,結束;為null的話就走到步驟2。
  2. 如果Handler的成員變數mCallback不為null,則呼叫mCallback.handleMessage(msg),結束;為null的話就走到步驟3。
  3. 呼叫HandlerhandleMessage(msg)方法,結束。

3.4 MessageQueue類

我們再來看下MessageQueue類,主要包括訊息入隊、取出訊息和退出等。

3.4.1 MessageQueue構造方法

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        //通過native方法去初始化化訊息佇列
        mPtr = nativeInit();
    }

3.4.2 訊息入隊

3.4.2.1 MessageQueue的enqueueMessage()
    boolean enqueueMessage(Message msg, long when) {
        //判斷訊息是否關聯了Handler,若無則拋異常
        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();
            //獲取message的when時間(觸發時間)
            msg.when = when;
            //獲取訊息佇列裡的訊息
            //Message是個連結串列結構
            Message p = mMessages;
            boolean needWake;
            //如果訊息佇列裡沒有訊息
            //或者發生時間(when)在連結串列的頭結點之前
            if (p == null || when == 0 || when < p.when) {
                //將訊息插入到連結串列的頭結點,即放入隊頭
                msg.next = p;
                mMessages = msg;
                //如果處於阻塞狀態,則喚醒
                needWake = mBlocked;
            } else {
                //如果訊息佇列中已存在訊息且觸發時間(when)在連結串列的頭結點之後
                //則插入到佇列中間

                //通常這裡的needWake為false,即不需喚醒訊息佇列
                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;
            }

            if (needWake) {//如果需要喚醒
                //在native層喚醒訊息佇列
                nativeWake(mPtr);
            }
        }
        return true;
    }

可以看到,訊息佇列是根據訊息觸發時間來進行排隊的,觸發時間最早的訊息將會排到佇列的頭部。當有新訊息需要加入訊息佇列時,會從佇列頭開始遍歷,直到找到訊息應該插入的合適位置,以保證所有訊息的時間順序。
如果訊息佇列需要喚醒,則會在訊息加入訊息佇列後對訊息佇列進行喚醒。

3.4.3 取出訊息

3.4.3.1 MessageQueue的next()
    Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            //當訊息迴圈已經退出,直接返回
            return null;
        }
        //只有首次迭代為-1
        int pendingIdleHandlerCount = -1; 
        int nextPollTimeoutMillis = 0;
        //死迴圈
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //這裡會阻塞,直到nextPollTimeoutMillis超時或者訊息佇列被喚醒
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    //當target為空時,在訊息佇列中迴圈查詢到下一條非同步訊息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //當訊息觸發時間大於當前時間
                    if (now < msg.when) {
                        //下一條訊息還沒準備好,設定一個超時喚醒
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 獲取一條訊息
                        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 {
                    //沒有訊息
                    nextPollTimeoutMillis = -1;
                }

                //如果訊息正在退出,返回null
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //如果當前是第一次迴圈時
                //且當前訊息佇列為空時,或者下一條訊息還沒準備好時
                //即當前處於空閒的狀態
                //那麼就獲取Idle Handler的數量
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    //沒有idle handlers 需要執行,繼續迴圈並等待。
                    mBlocked = true;
                    continue;
                }

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

            //執行idle handlers
            //只有第一次迴圈時才會執行這些程式碼塊
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                //釋放handler的引用
                mPendingIdleHandlers[i] = null; 

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

                if (!keep) {
                    synchronized (this) {
                        //根據idler.queueIdle()的返回值來判斷是否移除idler
                        //即返回true的話能夠重複執行
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            //重置IdleHandler的數量為0,這樣就保證不會重複執行
            //即只有第一次迴圈時會執行
            pendingIdleHandlerCount = 0;

            //重置超時時間為0
            //即當呼叫一個idle handler時, 一個新的訊息能夠被分發,因此無需等待即可回去繼續查詢還未被處理的訊息
            nextPollTimeoutMillis = 0;
        }
    }

取出訊息時,如果沒有訊息或者超時時間還沒到,則會處於阻塞的狀態,直到超時時間過去或者訊息佇列被喚醒。當訊息準備好時,才會返回訊息出去。
另外,如果當前處於空閒的狀態,則會執行IdleHandler中的方法。

3.4.4 訊息佇列退出

3.4.4.1 MessageQueue的quit()
    void quit(boolean safe) {
        //主執行緒的訊息佇列是不允許退出的,主要還是看mQuitAllowed的值
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            //如果正在退出,則直接返回,防止多次操作
            if (mQuitting) {
                return;
            }
            //設定為正在退出中
            mQuitting = true;
            //判斷是否安全移除
            if (safe) {
                //移除尚未觸發的所有訊息
                removeAllFutureMessagesLocked();
            } else {
                //移除所有的訊息
                removeAllMessagesLocked();
            }

            nativeWake(mPtr);
        }
    }

如果是安全退出,那麼只會移除尚未觸發的所有訊息,對於正在觸發的訊息並不移除;
如果不是安全退出,則直接移除所有的訊息。

4.一些面試題

這裡收集了一些常見的面試題來解答一下,大部分答案其實都能在上面的分析中找到的,所以嘛,要認真看程式碼。
這裡只列出一部分題目,如果有好的題目也可以留言補充哈~

4.1 Q:Looper.loop()是個死迴圈,主執行緒為什麼不會卡死?

對於一個執行緒,如果執行完程式碼之後就會正常退出。但是對於主執行緒,我們肯定是希望能夠一直存活的,那麼最簡單的方法就是寫個死迴圈讓它一直在執行,這樣就不會退出了。那麼主執行緒為什麼不會卡死呢?如果訊息佇列裡面有訊息,Looper就取出來出來;如果沒有訊息就會阻塞,直到有新訊息進來喚醒訊息佇列去處理,這一過程就是在這個死迴圈中處理的,所以說Looper本身是不會卡死的。像ActivityonCreate()onResume()等生命週期實際上就是在這個死迴圈中執行的。如果我們在ActivityonCreate()onResume()中做一些耗時操作,可能就會發生掉幀,甚至出現ANR,這才是我們看到的卡頓卡死現象。

4.2 Q:一個執行緒中是否可以有多個Handler?如果有多個,分發訊息是如何區分的?

是可以有多個的。我們使用Handler傳送Message時,Message中的target變數會儲存當前的Handler引用,分發訊息時就是靠這個target來區分不同的Handler

有問題請留言補充吧~~