Android消息機制分析:Handler、Looper、MessageQueue源碼分析

1.前言

關(guān)于Handler消息機制的博客實際上是非常多的了。
之前也是看別人的博客過來的,但是過了一段時間之后,一些細節(jié)也就忘了。
所以,就自己擼一篇,權(quán)當筆記,方便以后翻閱。

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

本文源碼基于android 27

2.Android消息機制概述

2.1 本質(zhì)

Android的消息機制本質(zhì)就是一套消息發(fā)送,傳遞及處理的機制。

2.2 角色說明

角色 作用
Handler
(處理者)
負責(zé)消息的發(fā)送及處理等等。
Message
(消息)
保存消息的內(nèi)容,如保存一個消息類型(what)等等。
MessageQueue
(消息隊列)
保存Message的數(shù)據(jù)結(jié)構(gòu),是一個鏈表。
Looper
(循環(huán)器)
從消息隊列中循環(huán)的取出消息然后把消息交給Handler處理。

2.3 原理簡介

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

  1. 應(yīng)用啟動時會在主線程中創(chuàng)建一個Looper(循環(huán)器),Looper(循環(huán)器)內(nèi)部則會創(chuàng)建一個MessageQueue(消息隊列);
  1. 然后Looper(循環(huán)器)就會不斷的輪詢MessageQueue(消息隊列)中是否有Message(消息);
  2. 我們可以通過Handler去發(fā)送一個Message(消息),發(fā)送之后Message(消息)就會進入到MessageQueue(消息隊列)中去,Looper(循環(huán)器)通過輪詢?nèi)〕?code>Message(消息),然后交給相應(yīng)的Handler(處理者)去處理。

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

上面這個流程總結(jié)成一句話就是:

Looper(循環(huán)器)通過不斷的從MessageQueue(消息隊列)中取出Message(消息),然后交給相應(yīng)的Handler(處理者)去處理。

是不是很簡單呢?

2.4 Handler的應(yīng)用--UI更新

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

在子線程中進行一些數(shù)據(jù)更新等耗時操作,然后使用Handler在主線程中去更新UI。

為什么要怎么操作呢?這里有兩個前提:

1.Android開發(fā)中規(guī)定了UI的更新只能在主線程中去操作,在子線程中更新會報錯。
2.我們在主線程中創(chuàng)建的Handler能夠接受到同一個Handler在子線程中發(fā)送的消息。

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

那么,為什么更新UI只能在主線程中去操作呢?

這是因為Android中的UI控件不是線程安全的,因此在多線程中并發(fā),可能會出現(xiàn)線程安全的問題,即訪問UI控件可能會出現(xiàn)跟預(yù)期不一樣的結(jié)果。那么為什么不使用鎖機制呢?因為加鎖會降低訪問UI的效率,鎖機制會阻塞某些線程的執(zhí)行。因此,最簡單高效的方法就是使用單線程模型來進行UI的訪問了。

那么,為什么主線程中的Handler能接受到其他線程發(fā)來的消息呢?
這是后面源碼分析的內(nèi)容,這里暫且不表。

2.5 Handler的其他應(yīng)用

上面UI更新實際上只是消息機制其中一個應(yīng)用場景。
如果我們了解四大組件的啟動停止等過程的話,就會發(fā)現(xiàn),都是在一個名為HHandler中處理狀態(tài)切換等邏輯,這個HActivityThread的內(nèi)部類。其本質(zhì)就是切到主線程中去處理。

所以說,不要將Handler僅僅局限于UI更新。

3.源碼分析

本節(jié)主要深入源碼對消息機制進行分析。對涉及到LooperMessageQueue、MessageHandler等類進行逐一分析。

3.1 Looper類

3.1.1 Looper(循環(huán)器)的創(chuàng)建

Looper的創(chuàng)建可以分為在主線程中創(chuàng)建以及在子線程中創(chuàng)建,我們分別來看下。

3.1.1.1 主線程中創(chuàng)建Looper

先來看下Looper在主線程中是什么時候創(chuàng)建的。

3.1.1.1.1 ActivityThread的main()

應(yīng)用啟動時,會調(diào)用到ActivityThread中的main()方法,這個main()方法是應(yīng)用程序的入口。main()里面會創(chuàng)建一個Looper對象出來。我們來看下代碼:

    public static void main(String[] args) {
        //省略無關(guān)代碼

        //為主線程創(chuàng)建1個Looper對象
        Looper.prepareMainLooper();
        
        //創(chuàng)建主線程
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        //省略無關(guān)代碼
        
        //開啟消息循環(huán)
        Looper.loop();
    }

可以看到,應(yīng)用啟動時就為主線程創(chuàng)建出一個Looper對象,并且開啟消息循環(huán)。

3.1.1.1.2 Looper的prepareMainLooper()

再來看下prepareMainLooper()

    public static void prepareMainLooper() {
        //最終還是調(diào)用prepare()
        //參數(shù)false表示主線程中的消息循環(huán)不允許退出
        prepare(false);

        //判斷sMainLooper是否為null,否則拋出異常
        //即prepareMainLooper()不能夠被調(diào)用兩次
        //保證了主線程中只存在一個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可以保存一個線程內(nèi)的局部變量
        //這里判斷當前線程是否已經(jīng)存在Looper對象,存在的話則拋異常
        //因為一個線程只能創(chuàng)建一個Looper對象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //當前線程沒有創(chuàng)建Looper對象的話
        //則新創(chuàng)建一個Looper對象
        //并把這個Looper對象保存到ThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }

prepare()中就是創(chuàng)建一個Looper對象并把Looper對象保存到線程中的ThreadLocal。

3.1.1.1.4 Looper的構(gòu)造方法

再來看下Looper的構(gòu)造方法:

    private Looper(boolean quitAllowed) {
        //創(chuàng)建消息隊列
        mQueue = new MessageQueue(quitAllowed);
        //記錄當前線程.
        mThread = Thread.currentThread();
    }

Looper內(nèi)部中就是創(chuàng)建了一個消息隊列。

3.1.1.1.5 小結(jié)

應(yīng)用啟動時,主線程會創(chuàng)建一個Looper對象出來,Looper內(nèi)部則創(chuàng)建消息隊列。

3.1.1.2 子線程中創(chuàng)建Looper

在子線程中創(chuàng)建Looper非常簡單,直接看例子吧:

3.1.1.2.1 子線程中創(chuàng)建Looper例子
class LooperThread extends Thread {
    public Handler mHandler;
 
    public void run() {
        Looper.prepare();
 
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                //TODO ...
            }
        };

        Looper.loop(); 
    }

調(diào)用一下無參數(shù)的prepare()方法即可。

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

最終還是調(diào)用有參數(shù)的prepare()方法,有參數(shù)的prepare()方法祥見上面的代碼分析。
true代表這個Looper是可以退出的。主線程中創(chuàng)建的Looper則是不能退出的。這就是他們的區(qū)別。

3.1.2 Looper(循環(huán)器)的消息循環(huán)

Looper是通過loop()這個方法來進行消息循環(huán)的,我們來看下代碼:

3.1.2.1 Looper的loop()
    public static void loop() {
        //獲得當前線程的Looper對象
        //myLooper()實際上通過sThreadLocal.get()來獲取的
        final Looper me = myLooper();
        //如果當前線程沒有創(chuàng)建過Looper,則拋出異常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //獲得Looper對象中的消息隊列
        final MessageQueue queue = me.mQueue;

        //死循環(huán)
        for (;;) {
            //從消息隊列中取出一個消息
            //如果消息隊列沒有消息的話,會阻塞在這里
            Message msg = queue.next(); // might block
            //消息為null的話表示停止消息循環(huán)
            //可以通過queue.quit()來停止,前提是通過prepare(true);來創(chuàng)建的
            //主線程中不允許停止消息循環(huán)
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //...
            
            //分發(fā)消息去處理
            //msg.target就是要處理的Handler,祥見后面分析
            msg.target.dispatchMessage(msg);

            //...

            //回收消息
            msg.recycleUnchecked();
        }
    }
3.1.2.2 小結(jié)

Looper中通過一個死循環(huán)從消息隊列中取消息,一旦取到消息之后,就分發(fā)交給Handler來處理。

3.1.3 Looper(循環(huán)器)的退出

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

退出Looper有兩個方法,如上。最終還是通過調(diào)用MessageQueue.quit(boolean safe)方法來實現(xiàn),只是傳的參數(shù)不一樣而已。這個在MessageQueue那一小節(jié)再來分析。

3.2 Message類

Message類用來保存消息的內(nèi)容。我們先來看下Message類會保存哪些消息:

3.2.1 Message類主要成員變量

成員變量 類型 含義
what int 消息類型
obj Object 消息內(nèi)容
when long 消息觸發(fā)時間
target Handler 消息處理者
callback Runnable 回調(diào)方法
sPool Message 消息池
sPoolSize int 消息池大小
next Message 下一條消息

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

3.2.2 獲取消息

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

3.2.2.1 Message的obtain()
    public static Message obtain() {
        synchronized (sPoolSync) {
            //如果消息池不為null,則從消息池中取出一條消息
            if (sPool != null) {
                //從sPool中取出頭結(jié)點
                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;
        }
        //調(diào)用recycleUnchecked()
        recycleUnchecked();
    }

調(diào)用recycleUnchecked()來回收。

3.2.3.2 Message的recycleUnchecked()
    void recycleUnchecked() {
        //將消息標記為使用狀態(tài)
        //清空消息其他參數(shù)
        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 小結(jié)

Message內(nèi)部維護了一個消息池,這個消息池是以鏈表的形式存在的。通過消息池去獲取Message對象能夠避免直接創(chuàng)建對象,可以起到一個提高效率的作用。

3.3 Handler類

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

我們先來看下Handler的構(gòu)造方法。

3.3.1 Handler的構(gòu)造方法

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

最終會調(diào)用有參數(shù)的構(gòu)造方法Handler(Callback callback, boolean async)

3.3.1.2 Handler的有參構(gòu)造方法

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

    public Handler(Callback callback, boolean async) {
        //匿名類、內(nèi)部類或本地類應(yīng)該申明為static,否則會警告可能出現(xiàn)內(nèi)存泄露
        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的所有有參構(gòu)造方法

    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,相關(guān)代碼就不貼了,還是很簡單的。

3.3.2 Handler發(fā)送消息

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

3.3.2.1 Handler的sendMessage()
    public final boolean sendMessage(Message msg)
    {   
        //調(diào)用sendMessageDelayed()
        return sendMessageDelayed(msg, 0);
    }
3.3.2.2 Handler的sendMessageDelayed()
   public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //調(diào)用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;
        }
        //調(diào)用enqueueMessage(),消息入隊
        return enqueueMessage(queue, msg, uptimeMillis);
    }
3.3.2.4 Handler的enqueueMessage()
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //把當前的Handler對象保存到消息中的target中去
        //這樣消息分發(fā)時才能找到相應(yīng)的Handler去處理
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //把消息放到消息隊列中去
        return queue.enqueueMessage(msg, uptimeMillis);
    }

最終,發(fā)送消息就是將消息放到消息隊列中去。

3.3.3 Handler發(fā)送Runnable

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

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

post()方法里面同樣也是通過sendMessageDelayed()來發(fā)送消息,我們來看下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來進行消息發(fā)送的。

3.3.4 分發(fā)消息

前面Looper在消息隊列中取到消息后就調(diào)用msg.target.dispatchMessage(msg);來分發(fā)消息,這里的msg.target就是Handler。我們來看下dispatchMessage()方法:

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

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

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

從上面的代碼可以看到,分發(fā)消息的流程如下:

  1. 如果msg.callback不為null,這個msg.callback實際是個Runnable對象,則調(diào)用這個Runnablerun()方法,結(jié)束;為null的話就走到步驟2。
  2. 如果Handler的成員變量mCallback不為null,則調(diào)用mCallback.handleMessage(msg),結(jié)束;為null的話就走到步驟3。
  3. 調(diào)用HandlerhandleMessage(msg)方法,結(jié)束。

3.4 MessageQueue類

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

3.4.1 MessageQueue構(gòu)造方法

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

3.4.2 消息入隊

3.4.2.1 MessageQueue的enqueueMessage()
    boolean enqueueMessage(Message msg, long when) {
        //判斷消息是否關(guān)聯(lián)了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();
                //返回失敗結(jié)果
                return false;
            }
            //標記消息為使用狀態(tài)
            msg.markInUse();
            //獲取message的when時間(觸發(fā)時間)
            msg.when = when;
            //獲取消息隊列里的消息
            //Message是個鏈表結(jié)構(gòu)
            Message p = mMessages;
            boolean needWake;
            //如果消息隊列里沒有消息
            //或者發(fā)生時間(when)在鏈表的頭結(jié)點之前
            if (p == null || when == 0 || when < p.when) {
                //將消息插入到鏈表的頭結(jié)點,即放入隊頭
                msg.next = p;
                mMessages = msg;
                //如果處于阻塞狀態(tài),則喚醒
                needWake = mBlocked;
            } else {
                //如果消息隊列中已存在消息且觸發(fā)時間(when)在鏈表的頭結(jié)點之后
                //則插入到隊列中間
                
                //通常這里的needWake為false,即不需喚醒消息隊列
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //死循環(huán),找到當前消息比鏈表中的消息早發(fā)生的消息,插入到那條消息前面,否則就插入到鏈表表尾
                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;
    }

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

3.4.3 取出消息

3.4.3.1 MessageQueue的next()
    Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            //當消息循環(huán)已經(jīng)退出,直接返回
            return null;
        }
        //只有首次迭代為-1
        int pendingIdleHandlerCount = -1; 
        int nextPollTimeoutMillis = 0;
        //死循環(huán)
        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為空時,在消息隊列中循環(huán)查找到下一條異步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //當消息觸發(fā)時間大于當前時間
                    if (now < msg.when) {
                        //下一條消息還沒準備好,設(shè)置一個超時喚醒
                        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);
                        //消息標記為使用狀態(tài)
                        msg.markInUse();
                        //返回消息
                        return msg;
                    }
                } else {
                    //沒有消息
                    nextPollTimeoutMillis = -1;
                }

                //如果消息正在退出,返回null
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //如果當前是第一次循環(huán)時
                //且當前消息隊列為空時,或者下一條消息還沒準備好時
                //即當前處于空閑的狀態(tài)
                //那么就獲取Idle Handler的數(shù)量
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    //沒有idle handlers 需要運行,繼續(xù)循環(huán)并等待。
                    mBlocked = true;
                    continue;
                }

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

            //運行idle handlers
            //只有第一次循環(huán)時才會執(zhí)行這些代碼塊
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                //釋放handler的引用
                mPendingIdleHandlers[i] = null; 

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

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

            //重置IdleHandler的數(shù)量為0,這樣就保證不會重復(fù)運行
            //即只有第一次循環(huán)時會運行
            pendingIdleHandlerCount = 0;

            //重置超時時間為0
            //即當調(diào)用一個idle handler時, 一個新的消息能夠被分發(fā),因此無需等待即可回去繼續(xù)查找還未被處理的消息
            nextPollTimeoutMillis = 0;
        }
    }

取出消息時,如果沒有消息或者超時時間還沒到,則會處于阻塞的狀態(tài),直到超時時間過去或者消息隊列被喚醒。當消息準備好時,才會返回消息出去。
另外,如果當前處于空閑的狀態(tài),則會執(zhí)行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;
            }
            //設(shè)置為正在退出中
            mQuitting = true;
            //判斷是否安全移除
            if (safe) {
                //移除尚未觸發(fā)的所有消息
                removeAllFutureMessagesLocked();
            } else {
                //移除所有的消息
                removeAllMessagesLocked();
            }

            nativeWake(mPtr);
        }
    }

如果是安全退出,那么只會移除尚未觸發(fā)的所有消息,對于正在觸發(fā)的消息并不移除;
如果不是安全退出,則直接移除所有的消息。

4.一些面試題

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

4.1 Q:Looper.loop()是個死循環(huán),主線程為什么不會卡死?

對于一個線程,如果執(zhí)行完代碼之后就會正常退出。但是對于主線程,我們肯定是希望能夠一直存活的,那么最簡單的方法就是寫個死循環(huán)讓它一直在執(zhí)行,這樣就不會退出了。那么主線程為什么不會卡死呢?如果消息隊列里面有消息,Looper就取出來出來;如果沒有消息就會阻塞,直到有新消息進來喚醒消息隊列去處理,這一過程就是在這個死循環(huán)中處理的,所以說Looper本身是不會卡死的。像ActivityonCreate()、onResume()等生命周期實際上就是在這個死循環(huán)中執(zhí)行的。如果我們在ActivityonCreate()、onResume()中做一些耗時操作,可能就會發(fā)生掉幀,甚至出現(xiàn)ANR,這才是我們看到的卡頓卡死現(xiàn)象。

4.2 Q:一個線程中是否可以有多個Handler?如果有多個,分發(fā)消息是如何區(qū)分的?

是可以有多個的。我們使用Handler發(fā)送Message時,Message中的target變量會保存當前的Handler引用,分發(fā)消息時就是靠這個target來區(qū)分不同的Handler。

有問題請留言補充吧~~

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

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

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