Android-Handler消息機制

Handler在Android中負責(zé)調(diào)度消息并將來某個時段處理消息。Android有大量的消息驅(qū)動方式來進行交互,比如四大組件的的啟動過程的交互,都離不開消息機制。

消息機制涉及MessageQueue/Message/Looper/Handler這4個類。

Message:消息分為硬件產(chǎn)生的消息(如按鈕、觸摸)和軟件生成的消息;

MessageQueue:消息隊列的主要功能向消息池投遞消息和取走消息池的消息;

Handler:消息輔助類,主要功能向消息池發(fā)送各種消息事件(Handler.sendMessage)和處理相應(yīng)消息事件(Handler.handleMessage);

Looper:不斷循環(huán)執(zhí)行(Looper.loop),按分發(fā)機制將消息分發(fā)給目標(biāo)處理者。

梳理過程從handler構(gòu)造函數(shù)開始,然后產(chǎn)生一條消息通過sendMessage向下分發(fā)。之后又是如何被調(diào)度,最后被怎樣處理。

 public Handler(@Nullable Callback callback, boolean async) {   
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

這里是通過Looper中的myLooper方法來獲得Looper實例的,如果Looper為null的話就會拋異常,拋出的異常內(nèi)容翻譯過來就是無法在未調(diào)用 Looper.prepare()的線程內(nèi)創(chuàng)建handler,new handler的時候沒有報錯那么就一定之前就調(diào)用了 Looper.Prepare(),在哪里創(chuàng)建的呢?

答案在 ActivityThread.main() 中。

ActivityThread 的 main()方法就是整個APP的入口,也就是我們通常所講的主線程, UI線程。但他實際上并不是一個線程,ActivityThread 并沒有繼承 Thread 類,我們可以把他理解為主線程的管理者,負責(zé)管理主線程的執(zhí)行,調(diào)度等操作,看下main方法的源碼。

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

兩個關(guān)鍵方法 Looper.prepareMainLooper()和Looper.loop(),loop方法后面再分析,繼續(xù)看prepareMainLooper的方法內(nèi)部如下:

  
   // 1
    public static void prepareMainLooper() {
        // 2
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
   // 2
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 3
        sThreadLocal.set(new Looper(quitAllowed));
    }
 
  // 3
  private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

到1??這里就解決了,為什么我們在主線程中使用Handler之前沒有調(diào)用Looper.prepare方法的問題了,就是在這里調(diào)用了prepare 方法。
2??prepare方法內(nèi)部把 Looper 放到ThreadLocal中與當(dāng)前線程綁定,保證一個線程只有一個Looper實例,同時一個Looper實例也只有一個MessageQueue。
3??在Looper的構(gòu)造函數(shù)中始化MessageQueue

所以初始化Handler之前Looper 和 MessageQueue就已經(jīng)初始化了,并把Looper放到ThreadLocal中。ThreadLocal有個特點是你set進去的值是以當(dāng)前所處的線程為key,value就是你set的值。源碼如下:

public void set(T value) {
       Thread t = Thread.currentThread();
       ThreadLocalMap map = getMap(t);
       if (map != null)
           map.set(this, value);
       else
           createMap(t, value);
   }

Handler的sendMessage方法都做了什么

先看一下sendMessage的流程圖:


sendMessage

簡單過一遍,sendXXX 這些方式最終還是會調(diào)用到 enqueueMessage 這個方法上來的邏輯最后調(diào)用queue.enqueueMessage 添加消息到消息隊列MessageQueue中,下面就是消息的入隊邏輯

    boolean enqueueMessage(Message msg, long when) {
            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 {
                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;
            }
        }
        return true;
    }

消息入隊順序是按照 Message 觸發(fā)時間 long when入隊的,有新消息加入時,會循環(huán)遍歷當(dāng)前消息隊列,對比消息觸發(fā)時間,直到找到當(dāng)前消息合適的插入位置,以此來保證所有消息的觸發(fā)時間順序。即 MessageQueue 添加消息到消息隊列中。

所以消息的存儲與管理MessageQueue 來負責(zé)

誰負責(zé)把消息分發(fā)出去

Handler把Message交給MessageQueue并存儲起來 ,那么誰來負責(zé)把我這里的消息分發(fā)出去并且告訴主線程的人(Handler)呢?答案就是 Looper 了!


Looper

之前分析的ActivityThread.main()中的Looper.loop還沒有講到我們接著跟進去看一下源碼

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
       // 循環(huán)調(diào)用 MessageQueue 的 next 方法獲取消息
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        try {
            msg.target.dispatchMessage(msg);
        }
    }
}

loop()不斷從MessageQueue中取消息,把消息交給target(handler)的dispatchMessage方法處理。下面是dispatchMessage

public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

可以看到dispatchMessage最后調(diào)用了handleMessage方法,在源碼里面是個空實現(xiàn)。需要我們在創(chuàng)建handler的時候復(fù)寫,根據(jù)不同的message處理不同的業(yè)務(wù)邏輯。

整個Handler的消息分發(fā)調(diào)度流程就講完了,為了增強記憶,我再把整個流程拼成一塊如下圖:


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)容