Android消息機(jī)制

消息機(jī)制

主線程和子線程通信
消息機(jī)制涉及到三個(gè)角色,Handler、MessageQueue、Looper

基本實(shí)現(xiàn)

這里只介紹主線程handler創(chuàng)建方式,子線程后續(xù)源碼部分在介紹

  • 創(chuàng)建Handler,重寫(xiě)handleMessage方法
  • Handler發(fā)送消息
  • handleMessage接收消息并處理
基本原理

Handler發(fā)送消息并最終添加到MessageQueue,Looper調(diào)用loop方法后開(kāi)始輪詢從MessageQueue中獲取消息并調(diào)用對(duì)應(yīng)的handler的dispatchMessage方法。

疑問(wèn)
  • Handler如何發(fā)送消息的
  • Looper是如何初始化,如何獲取消息的
  • 消息機(jī)制如何實(shí)現(xiàn)線程間如何切換的
  • Looper死循環(huán)為什么不會(huì)導(dǎo)致anr
  • Linux的epoll機(jī)制

帶著疑問(wèn)我們通過(guò)下面源碼部分去解惑

源碼解析
  • Handler到底是如何發(fā)送消息的
    首先來(lái)看Hander創(chuàng)建
    public Handler(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;
    }

創(chuàng)建時(shí)會(huì)調(diào)用Looper.myLooper()獲取當(dāng)前線程的Looper對(duì)象

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
}

這里用到了ThreadLocal線程局部變量,內(nèi)部維護(hù)著一個(gè)ThreadLocalMap以map形式存儲(chǔ)局部變量,key是當(dāng)前線程value是局部變量。這樣就實(shí)現(xiàn)了線程之間數(shù)據(jù)隔離。避免數(shù)據(jù)沖突。

創(chuàng)建完Handler以后繼續(xù)看發(fā)送消息SendMessage
 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);
    }

sendMessage會(huì)調(diào)用sendMessageDelayed緊接著又調(diào)用SendMessageAtTime,最終會(huì)調(diào)用enqueueMessage,將消息添加到消息隊(duì)列。這里消息隊(duì)列是哪里來(lái)的呢,在我們最開(kāi)始Handler初始化時(shí)有一行代碼mQueue = mLooper.mQueue;原來(lái)是來(lái)自Looper的MessageQueue,具體它的創(chuàng)建我們?cè)诤竺鍸ooper初始化在詳解。
接下來(lái)在看enqueueMessage如何將消息添加到消息隊(duì)列

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler的enqueueMessage做了兩件事情,第一是將當(dāng)前handler引用賦值給message的target建立綁定關(guān)系以便于后面消息分發(fā)處理的時(shí)候知道要處理的消息屬于哪個(gè)Handler。第二件事情就是調(diào)用MessageQueue的enqueueMessage

 boolean enqueueMessage(Message msg, long when) {
        //...忽略部分代碼

        synchronized (this) {
           //... 忽略部分代碼

            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;
    }

MessageQueue的enqueueMessage中會(huì)判斷當(dāng)前message是否需要立即執(zhí)行如果需要就添加到表頭,否則就根據(jù)時(shí)間輪詢比對(duì)添加到中間位置。
到這里Handler創(chuàng)建和消息發(fā)送以及添加到消息隊(duì)列過(guò)程就很清晰了,接下來(lái)要看Looper的創(chuàng)建和消息如何輪詢分發(fā)了。

  • Looper是如何初始化,如何獲取消息的
    這里又要回到Handler的初始化,在Handler初始化的時(shí)候回獲取當(dāng)前線程的Looper對(duì)象
public Handler(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對(duì)象為空就會(huì)拋出異常,所以Handler初始化之前必須要先初始化Looper,但是我們平時(shí)主線程并沒(méi)有手動(dòng)創(chuàng)建為什么不會(huì)報(bào)錯(cuò)呢?這是因?yàn)樵赼pp啟動(dòng)入口ActivityThread的main方法中系統(tǒng)已經(jīng)為我們做好了主線程的Looper初始化

public static void main(String[] args) {
     //...忽略部分代碼
        Looper.prepareMainLooper();
    //...忽略部分代碼
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

那Looper到底是如何初始化的呢,接著看Looper的prepareMainLooper

  public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepareMainLooper又調(diào)用了prepare方法

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

原來(lái)Looper的初始化是調(diào)用prepare方法,這里會(huì)創(chuàng)建looper對(duì)象并添加到ThreadLocal中,所以一個(gè)線程只能有一個(gè)Looper,并且prepare只能調(diào)用一次否則會(huì)報(bào)錯(cuò),
那Looper是如何獲取消息進(jìn)行分發(fā)的呢,在上面ActivityThread代碼中我們也可以看到Looper初始化完以后會(huì)調(diào)用Looper.loop()真相就在這里

public static void loop() {
        final Looper me = myLooper();
        //... 忽略部分代碼
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            //...忽略部分代碼
            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
          //...忽略部分代碼
            msg.recycleUnchecked();
        }
    }

我們可以看到loop方法輪詢從MessageQueue中取出消息,判斷如果需要立即執(zhí)行就會(huì)調(diào)用meesage的target的dispatchtMessage方法進(jìn)行消息分發(fā)處理。message的target就是上面提到的message所屬的handler,因此會(huì)回調(diào)到Handler的dispatchMessage方法

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

dispatchMessage會(huì)判斷message的callback是否有設(shè)置,查看源碼可以知道setCallBack是hide方法只能通過(guò)反射調(diào)用它,如果它為空繼續(xù)判斷mCallBack是否為空,mCallBack哪兒來(lái)的呢,在Handler初始化時(shí)候有一個(gè)參數(shù)就是是否設(shè)置callback

public Handler(Callback callback, boolean async) {
        //...忽略部分代碼
        mCallback = callback;
    //...忽略部分代碼
    }

如果mCallBack有設(shè)置會(huì)直接調(diào)用CallBack的handleMessge方法否則繼續(xù)調(diào)用handler的handlemessage方法,因?yàn)槲覀冊(cè)诔跏蓟疕andler的時(shí)候回重寫(xiě)handlemessage方法所以最終會(huì)回調(diào)到我們重寫(xiě)的handlemessage方法里。

  • 消息機(jī)制是如何實(shí)現(xiàn)線程間切換的?
    我們通常會(huì)使用handler在子線程發(fā)送消息,然后回調(diào)到主線程處理消息。那到底是如何實(shí)現(xiàn)線程切換的呢,我們通過(guò)前面的分析可以知道,一個(gè)完整的消息分發(fā)流程,應(yīng)該是先調(diào)用Looper.prepare()初始化Looper和消息隊(duì)列,然后調(diào)用Looper.loop()開(kāi)啟輪詢。然后創(chuàng)建Handler在指定線程發(fā)送消息接受回調(diào)處理消息。那回調(diào)是在哪個(gè)線程呢,重點(diǎn)來(lái)了:因?yàn)橄⒎职l(fā)是Looper通過(guò)輪詢拿到Message的target也就是發(fā)送者Handler在調(diào)用Handler的dispatchMessage方法完成分發(fā)的,這個(gè)調(diào)用是在Looper中,所以調(diào)用者在哪個(gè)線程當(dāng)前回調(diào)就是在哪個(gè)線程,所以Looper初始化時(shí)綁定的線程就是回調(diào)的線程。

  • Looper死循環(huán)為什么不會(huì)導(dǎo)致anr
    App本質(zhì)上也是一個(gè)java程序,入口就是ActivityThread的main方法,如果main方法執(zhí)行完程序就推出了,如何保證不退出就是寫(xiě)一個(gè)死循環(huán),ActivityThread中初始化了Looper并調(diào)用了loop在loop方法中開(kāi)啟了一個(gè)死循環(huán)阻塞了主線程這樣程序可以保證程序一直執(zhí)行不會(huì)退出。幾乎所有的GUI程序都是這么實(shí)現(xiàn)的。既然是死循環(huán)那么其他代碼怎么運(yùn)行,頁(yè)面交互怎么處理呢?Android是基于事件驅(qū)動(dòng)的,不管是頁(yè)面刷新還是交互本質(zhì)上都是事件,都會(huì)被封裝成Message發(fā)送到MessageQueue由Looper進(jìn)行分發(fā)處理的。ANR是什么,Application no responding應(yīng)用無(wú)響應(yīng),為什么沒(méi)響應(yīng),因?yàn)橹骶€程做了好事操作,loop方法死循環(huán)也會(huì)阻塞主線程為什么不會(huì)anr,什么是響應(yīng),響應(yīng)就是頁(yè)面刷新,交互處理等,誰(shuí)來(lái)響應(yīng),其實(shí)就是looper的loop方法,,主線程做了耗時(shí)操作會(huì)阻塞loop方法導(dǎo)致無(wú)法處理其他message所以導(dǎo)致anr。

  • Linux的epoll機(jī)制
    我們先來(lái)看下Message的next方法

Message next() {
       //...省略部分代碼
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //重點(diǎn)關(guān)注這一行
            nativePollOnce(ptr, nextPollTimeoutMillis);

           //...省略部分代碼
        }
    }

在MessageQueue的next方法中會(huì)調(diào)用本地方法nativePollOnce,它是以阻塞的方式從native層的MessageQueue中獲取可用消息,也就是會(huì)進(jìn)行休眠。當(dāng)有可用消息時(shí)進(jìn)行喚醒。Looper的休眠和喚醒的實(shí)現(xiàn)原理是Linux的epoll機(jī)制,Looper的休眠和喚醒是通過(guò)對(duì)文件可讀事件監(jiān)聽(tīng)來(lái)實(shí)現(xiàn)喚醒。
什么是Linux的epoll機(jī)制?
Linux的epoll機(jī)制可以簡(jiǎn)單理解為事件監(jiān)聽(tīng),當(dāng)空閑的時(shí)候讓出cpu進(jìn)行休眠,當(dāng)事件觸發(fā)的時(shí)候會(huì)被喚醒開(kāi)始處理任務(wù)。

參考:看完這篇還不明白Handler你砍我

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 在Android應(yīng)用中,消息機(jī)制可謂是處于舉足輕重的地步,因?yàn)閁I是Android的整個(gè)門(mén)面展示,而UI的展示是交...
    JeromeLiee閱讀 520評(píng)論 1 3
  • 一、消息機(jī)制簡(jiǎn)述 消息機(jī)制是Android的核心,宏觀來(lái)說(shuō)它是一種順序的、非阻塞的任務(wù)機(jī)制,APP的主線程就是以消...
    東方未曦閱讀 632評(píng)論 0 0
  • 1. Android 消息機(jī)制概述 閱讀本文之前,你需要知道一下幾點(diǎn): 1.Handler的使用必須依賴于一個(gè)Lo...
    Android_Simon閱讀 1,191評(píng)論 0 4
  • use for 相信于此,絕大多數(shù)同學(xué)都會(huì)回答消息機(jī)制是android 為了線程間通信而引入的工具??梢暂p松的將一...
    漫步_蝸牛閱讀 522評(píng)論 0 1
  • 推薦指數(shù): 6.0 書(shū)籍主旨關(guān)鍵詞:特權(quán)、焦點(diǎn)、注意力、語(yǔ)言聯(lián)想、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析,社會(huì)...
    Jenaral閱讀 5,970評(píng)論 0 5

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