Android 消息處理機(jī)制(Looper、Handler、MessageQueue,Message)

Android 消息處理機(jī)制估計(jì)都被寫爛了,但是依然還是要寫一下,因?yàn)锳ndroid應(yīng)用程序是通過(guò)消息來(lái)驅(qū)動(dòng)的,Android某種意義上也可以說(shuō)成是一個(gè)以消息驅(qū)動(dòng)的系統(tǒng),UI、事件、生命周期都和消息處理機(jī)制息息相關(guān),并且消息處理機(jī)制在整個(gè)Android知識(shí)體系中也是尤其重要,在太多的源碼分析的文章講得比較繁瑣,很多人對(duì)整個(gè)消息處理機(jī)制依然是懵懵懂懂,這篇文章通過(guò)一些問(wèn)答的模式結(jié)合Android主線程(UI線程)的工作原理來(lái)講解,源碼注釋很全,還有結(jié)合流程圖,如果你對(duì)Android 消息處理機(jī)制還不是很理解,我相信只要你靜下心來(lái)耐心的看,肯定會(huì)有不少的收獲的。

概述#


1、我們先說(shuō)下什么是Android消息處理機(jī)制?

消息處理機(jī)制本質(zhì):一個(gè)線程開啟循環(huán)模式持續(xù)監(jiān)聽并依次處理其他線程給它發(fā)的消息。

簡(jiǎn)單的說(shuō):一個(gè)線程開啟一個(gè)無(wú)限循環(huán)模式,不斷遍歷自己的消息列表,如果有消息就挨個(gè)拿出來(lái)做處理,如果列表沒(méi)消息,自己就堵塞(相當(dāng)于wait,讓出cpu資源給其他線程),其他線程如果想讓該線程做什么事,就往該線程的消息隊(duì)列插入消息,該線程會(huì)不斷從隊(duì)列里拿出消息做處理。

2、Android消息處理機(jī)制的工作原理?

打個(gè)比方:公司類比App

  • PM 的主要工作是設(shè)計(jì)產(chǎn)品,寫需求文檔,改需求,中途改需求,提測(cè)前改需求...
  • UI 主要工作是UI設(shè)計(jì),交互等。
  • RD 工作我就不說(shuō)了
  • CEO 不解釋。

公司開創(chuàng)之后(App啟動(dòng)),那么CEO開始干活了(主線程【UI線程】啟動(dòng)),這時(shí)候CEO開啟了無(wú)限循環(huán)工作狂模式,自己的公司沒(méi)辦法?。ㄏ喈?dāng)于UI主線程轉(zhuǎn)成Looper線程【源碼里面有】)CEO招了一名RD(new Handler 實(shí)例)并把告訴PM和UI,如果你們有什么任務(wù)和需求就讓RD(Handler實(shí)例)轉(zhuǎn)告給我(CEO)。RD會(huì)把PM和UI的需求(Message)一條條記到CEO的備忘錄里(MessageQueue)。CEO 無(wú)限循環(huán)的工作就是不斷查看備忘錄,看有什么任務(wù)要做,有任務(wù)就從備忘錄一條一條拿出任務(wù)來(lái),然后交給這一名RD(Handler 實(shí)例)去處理(畢竟CEO 不會(huì)寫代碼 囧...)。當(dāng)然如果備忘錄都做完了,這時(shí)候CEO就會(huì)去睡覺(jué)(線程堵塞【簡(jiǎn)單理解成線程wait】,讓出CPU資源,讓其他線程去執(zhí)行)。但是這個(gè)備忘錄有個(gè)特殊的功能就是沒(méi)有任務(wù)的時(shí)候突然插入第一條任務(wù)(從無(wú)到有)就會(huì)有鬧鐘功能叫醒CEO起床繼續(xù)處理備忘錄。 整個(gè)消息處理機(jī)制的工作原理基本就是這樣的。后面會(huì)有源碼分析,你再來(lái)結(jié)合這個(gè)場(chǎng)景,會(huì)更好理解一些。

這里先給一張Android消息處理機(jī)制流程圖和具體執(zhí)行動(dòng)畫,如果看不懂沒(méi)事,接著往下看(后面會(huì)結(jié)合Android UI主線程來(lái)講解),然后結(jié)合著圖和動(dòng)畫一塊看更能理解整個(gè)機(jī)制的實(shí)現(xiàn)原理。



3、Looper、HandlerMessageQueue,Message作用和存在的意義?

  • **Looper **
    我們知道一個(gè)線程是一段可執(zhí)行的代碼,當(dāng)可執(zhí)行代碼執(zhí)行完成后,線程生命周期便會(huì)終止,線程就會(huì)退出,那么做為App的主線程,如果代碼段執(zhí)行完了會(huì)怎樣?,那么就會(huì)出現(xiàn)App啟動(dòng)后執(zhí)行一段代碼后就自動(dòng)退出了,這是很不合理的。所以為了防止代碼段被執(zhí)行完,只能在代碼中插入一個(gè)死循環(huán),那么代碼就不會(huì)被執(zhí)行完,然后自動(dòng)退出,怎么在在代碼中插入一個(gè)死循環(huán)呢?那么Looper出現(xiàn)了,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會(huì)變當(dāng)前線程變成Looper線程(可以先簡(jiǎn)單理解:無(wú)限循環(huán)不退出的線程),Looper.loop()方法里面有一段死循環(huán)的代碼,所以主線程會(huì)進(jìn)入while(true){...}的代碼段跳不出來(lái),但是主線程也不能什么都不做吧?其實(shí)所有做的事情都在while(true){...}里面做了,主線程會(huì)在死循環(huán)中不斷等其他線程給它發(fā)消息(消息包括:Activity啟動(dòng),生命周期,更新UI,控件事件等),一有消息就根據(jù)消息做相應(yīng)的處理,Looper的另外一部分工作就是在循環(huán)代碼中會(huì)不斷從消息隊(duì)列挨個(gè)拿出消息給主線程處理。

  • **MessageQueue **
    MessageQueue 存在的原因很簡(jiǎn)單,就是同一線程在同一時(shí)間只能處理一個(gè)消息,同一線程代碼執(zhí)行是不具有并發(fā)性,所以需要隊(duì)列來(lái)保存消息和安排每個(gè)消息的處理順序。多個(gè)其他線程往UI線程發(fā)送消息,UI線程必須把這些消息保持到一個(gè)列表(它同一時(shí)間不能處理那么多任務(wù)),然后挨個(gè)拿出來(lái)處理,這種設(shè)計(jì)很簡(jiǎn)單,我們平時(shí)寫代碼其實(shí)也經(jīng)常這么做。每一個(gè)Looper線程都會(huì)維護(hù)這樣一個(gè)隊(duì)列,而且僅此一個(gè),這個(gè)隊(duì)列的消息只能由該線程處理。

  • Handler **
    簡(jiǎn)單說(shuō)Handler用于同一個(gè)進(jìn)程的線程間通信。Looper讓主線程無(wú)限循環(huán)地從自己的MessageQueue拿出消息處理,既然這樣我們就知道
    處理消息肯定是在主線程中處理的,那么怎樣在其他的線程往主線程的隊(duì)列里放入消息呢?其實(shí)很簡(jiǎn)單,我們知道在同一進(jìn)程中線程和線程之間資源是共享的,也就是對(duì)于任何變量在任何線程都是可以訪問(wèn)和修改的,只要考慮并發(fā)性做好同步就行了,那么只要拿到MessageQueue 的實(shí)例,就可以往主線程的MessageQueue放入消息,主線程在輪詢的時(shí)候就會(huì)在主線程**處理這個(gè)消息。那么怎么拿到主線程 MessageQueue的實(shí)例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為了統(tǒng)一添加消息和消息的回調(diào)處理,又專門構(gòu)建了Handler類,你只要在主線程構(gòu)建Handler類,那么這個(gè)Handler實(shí)例就獲取主線程MessageQueue實(shí)例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的時(shí)候就通過(guò)這個(gè)引用往消息隊(duì)列里插入新消息。Handler 的另外一個(gè)作用,就是能統(tǒng)一處理消息的回調(diào)。這樣一個(gè)Handler發(fā)出消息又確保消息處理也是自己來(lái)做,這樣的設(shè)計(jì)非常的贊。具體做法就是在隊(duì)列里面的Message持有Handler的引用(哪個(gè)handler 把它放到隊(duì)列里,message就持有了這個(gè)handler的引用),然后等到主線程輪詢到這個(gè)message的時(shí)候,就來(lái)回調(diào)我們經(jīng)常重寫的Handler的handleMessage(Message msg)方法。

  • **Message **
    Message 很簡(jiǎn)單了,你想讓主線程做什么事,總要告訴它吧,總要傳遞點(diǎn)數(shù)據(jù)給它吧,Message就是這個(gè)載體。

源碼分析#


接下來(lái)我們會(huì)結(jié)合App主線程(UI線程)來(lái)講解,從App啟動(dòng)后一步一步往下走分析整個(gè)Android的消息處理機(jī)制,首先在ActivityThread類有我們熟悉的main的函數(shù),App啟動(dòng)的代碼的入口就在這里,UI線程本來(lái)只是一個(gè)普通線程,在這里會(huì)把UI線程轉(zhuǎn)換成Looper線程,什么是Looper線程,不急往下看就知道了。

public final class ActivityThread {
    public static final void main(String[] args) {
        ......
        Looper.prepareMainLooper();
        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {    
            sMainThreadHandler = thread.getHandler();
        }
        ......
        Looper.loop();
        ......
    }
}

首先執(zhí)行的是 Looper.prepareMainLooper() 我們來(lái)看下Looper里面的這個(gè)方法做了什么?

注:看之前先稍微了解下ThreadLocal是什么?
ThreadLocal: 線程本地存儲(chǔ)區(qū)(Thread Local Storage,簡(jiǎn)稱為TLS),每個(gè)線程都有自己的私有的本地存儲(chǔ)區(qū)域,不同線程之間彼此不能訪問(wèn)對(duì)方的TLS區(qū)域。這里線程自己的本地存儲(chǔ)區(qū)域存放是線程自己的Looper。具體看下ThreadLocal.java 的源碼!

public final class Looper {
    // sThreadLocal 是static的變量,可以先簡(jiǎn)單理解它相當(dāng)于map,key是線程,value是Looper,
    //那么你只要用當(dāng)前的線程就能通過(guò)sThreadLocal獲取當(dāng)前線程所屬的Looper。
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //主線程(UI線程)的Looper 單獨(dú)處理,是static類型的,通過(guò)下面的方法getMainLooper() 
    //可以方便的獲取主線程的Looper。
    private static Looper sMainLooper; 

    //Looper 所屬的線程的消息隊(duì)列
    final MessageQueue mQueue;
    //Looper 所屬的線程
    final Thread mThread;

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
         //如果線程的TLS已有數(shù)據(jù),則會(huì)拋出異常,一個(gè)線程只能有一個(gè)Looper,prepare不能重復(fù)調(diào)用。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //往線程的TLS插入數(shù)據(jù),簡(jiǎn)單理解相當(dāng)于map.put(Thread.currentThread(),new Looper(quitAllowed));
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //實(shí)際上是調(diào)用  prepare(false),并然后給sMainLooper賦值。
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    //static 方法,方便獲取主線程的Looper.
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }
    
    public static @Nullable Looper myLooper() {
        //具體看ThreadLocal類的源碼的get方法,
        //簡(jiǎn)單理解相當(dāng)于map.get(Thread.currentThread()) 獲取當(dāng)前線程的Looper
        return sThreadLocal.get();
    }
}

看了上面的代碼(仔細(xì)看下注釋),我們發(fā)現(xiàn) Looper.prepareMainLooper()做的事件就是new了一個(gè)Looper實(shí)例并放入Looper類下面一個(gè)static的ThreadLocal<Looper> sThreadLocal靜態(tài)變量中,同時(shí)給sMainLooper賦值,給sMainLooper賦值是為了方便通過(guò)Looper.getMainLooper()快速獲取主線程的Looper,sMainLooper是主線程的Looper可能獲取會(huì)比較頻繁,避免每次都到 sThreadLocal 去查找獲取。

接下來(lái)重點(diǎn)是看下Looper的構(gòu)造函數(shù),看看在new Looper的時(shí)候做了什么事?

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

看到?jīng)]有,這時(shí)候給當(dāng)前線程創(chuàng)建了消息隊(duì)列MessageQueue,并且讓Looper持有MessageQueue的引用。執(zhí)行完Looper.prepareMainLooper() 之后,主線程從普通線程轉(zhuǎn)成一個(gè)Looper線程。目前的主線程線程已經(jīng)有一個(gè)Looper對(duì)象和一個(gè)消息隊(duì)列mQueue,引用關(guān)系如下圖:(主線程可以輕松獲取它的Looper,主線程的Looper持有主線程消息隊(duì)列的引用)


具體如何獲取主線程的Looper對(duì)象和消息列表呢?

//在主線程中執(zhí)行
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue
//或者
mLooper=Looper.getMainLooper()

接下來(lái)回到ActivityThread 的main函數(shù),執(zhí)行完Looper.prepareMainLooper() 之后下一句代碼是ActivityThread thread = new ActivityThread();這句話就是創(chuàng)建一下ActivityThread對(duì)象,這邊需要注意的時(shí)候ActivityThread并不是一個(gè)線程,它并沒(méi)有繼承Thread,而只是一個(gè)普通的類public final class ActivityThread{...}ActivityThread的構(gòu)造函數(shù)并沒(méi)有做什么事只是初始化了資源管理器。

 ActivityThread() {
     mResourcesManager = ResourcesManager.getInstance();
 }

接著往下看下一行代碼

ActivityThread thread = new ActivityThread();
//建立Binder通道 (創(chuàng)建新線程)
thread.attach(false);

thread.attach(false);便會(huì)創(chuàng)建一個(gè)Binder線程(具體是指ApplicationThread,該Binder線程會(huì)通過(guò)想 HandlerMessage發(fā)送給主線程,之后講)。我們之前提到主線程最后會(huì)進(jìn)入無(wú)限循環(huán)當(dāng)中,如果沒(méi)有在進(jìn)入死循環(huán)之前創(chuàng)建其他線程,那么待會(huì)誰(shuí)會(huì)給主線程發(fā)消息呢?,沒(méi)錯(cuò)就是在這里創(chuàng)建了這個(gè)線程,這個(gè)線程會(huì)接收來(lái)自系統(tǒng)服務(wù)發(fā)送來(lái)的一些事件封裝了Message并發(fā)送給主線程,主線程在無(wú)限循環(huán)中從隊(duì)列里拿到這些消息并處理這些消息。(Binder線程發(fā)生的消息包括LAUNCH_ACTIVITY,PAUSE_ACTIVITY 等等)

繼續(xù)回到mian 函數(shù)的下一句代碼Looper.loop() 那么重點(diǎn)來(lái)了,我們來(lái)看下Looper.loop()的源碼:

public static void loop() {
    final Looper me = myLooper();  //獲取TLS存儲(chǔ)的Looper對(duì)象,獲取當(dāng)前線程的Looper 
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
 
    final MessageQueue queue = me.mQueue;  //獲取Looper對(duì)象中的消息隊(duì)列
    ....

    for (;;) { //主線程開啟無(wú)限循環(huán)模式
        Message msg = queue.next(); //獲取隊(duì)列中的下一條消息,可能會(huì)線程阻塞
        if (msg == null) { //沒(méi)有消息,則退出循環(huán),退出消息循環(huán),那么你的程序也就可以退出了
            return;
        }
        ....
        //分發(fā)Message,msg.target 是一個(gè)Handler對(duì)象,哪個(gè)Handler把這個(gè)Message發(fā)到隊(duì)列里,
        //這個(gè)Message會(huì)持有這個(gè)Handler的引用,并放到自己的target變量中,這樣就可以回調(diào)我們重寫
        //的handler的handleMessage方法。
        msg.target.dispatchMessage(msg);
        ....
        ....
        msg.recycleUnchecked();  //將Message回收到消息池,下次要用的時(shí)候不需要重新創(chuàng)建,obtain()就可以了。
    }
}

上面的代碼,大家具體看下注釋,這時(shí)候主線程(UI線程)執(zhí)行到這一步就進(jìn)入了死循環(huán),不斷地去拿消息隊(duì)列里面的消息出來(lái)處理?那么問(wèn)題來(lái)了
1、UI線程一直在這個(gè)循環(huán)里跳不出來(lái),主線程不會(huì)因?yàn)長(zhǎng)ooper.loop()里的死循環(huán)卡死嗎,那還怎么執(zhí)行其他的操作呢?

  • 在looper啟動(dòng)后,主線程上執(zhí)行的任何代碼都是被looper從消息隊(duì)列里取出來(lái)執(zhí)行的。也就是說(shuō)主線程之后都是通過(guò)其他線程給它發(fā)消息來(lái)實(shí)現(xiàn)執(zhí)行其他操作的。生命周期的回調(diào)也是如此的,系統(tǒng)服務(wù)ActivityManagerService通過(guò)Binder發(fā)送IPC調(diào)用給APP進(jìn)程,App進(jìn)程接到到調(diào)用后,通過(guò)App進(jìn)程的Binder線程給主線程的消息隊(duì)列插入一條消息來(lái)實(shí)現(xiàn)的。

2、主線程是UI線程和用戶交互的線程,優(yōu)先級(jí)應(yīng)該很高,主線程的死循環(huán)一直運(yùn)行是不是會(huì)特別消耗CPU資源嗎?App進(jìn)程的其他線程怎么辦?

  • 這基本是一個(gè)類似生產(chǎn)者消費(fèi)者的模型,簡(jiǎn)單說(shuō)如果在主線程的MessageQueue沒(méi)有消息時(shí),就會(huì)阻塞在loop的queue.next()方法里,這時(shí)候主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài),直到有下個(gè)消息進(jìn)來(lái)時(shí)候就會(huì)喚醒主線程,在2.2 版本以前,這套機(jī)制是用我們熟悉的線程的wait和notify 來(lái)實(shí)現(xiàn)的,之后的版本涉及到Linux pipe/epoll機(jī)制,通過(guò)往pipe管道寫端寫入數(shù)據(jù)來(lái)喚醒主線程工作。原理類似于I/O,讀寫是堵塞的,不占用CPU資源。

所以上面代碼的重點(diǎn)是queue.next() 的函數(shù),其他的我們就不多說(shuō)了,我們來(lái)看下queue.next()的源碼(主要還是看注釋):

Message next() 
       
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        int pendingIdleHandlerCount = -1; // -1 only during first iteration

        //nextPollTimeoutMillis 表示nativePollOnce方法需要等待nextPollTimeoutMillis 
        //才會(huì)返回
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //讀取消息,隊(duì)里里沒(méi)有消息有可能會(huì)堵塞,兩種情況該方法才會(huì)返回(代碼才能往下執(zhí)行)
            //一種是等到有消息產(chǎn)生就會(huì)返回,
            //另一種是當(dāng)?shù)攘薾extPollTimeoutMillis時(shí)長(zhǎng)后,nativePollOnce也會(huì)返回
            nativePollOnce(ptr, nextPollTimeoutMillis);
            //nativePollOnce 返回之后才能往下執(zhí)行
            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) {
                    // 循環(huán)找到一條不是異步而且msg.target不為空的message
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                       // 雖然有消息,但是還沒(méi)有到運(yùn)行的時(shí)候,像我們經(jīng)常用的postDelay,
                       //計(jì)算出離執(zhí)行時(shí)間還有多久賦值給nextPollTimeoutMillis,
                       //表示nativePollOnce方法要等待nextPollTimeoutMillis時(shí)長(zhǎng)后返回
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 獲取到消息
                        mBlocked = false;
                       //鏈表一些操作,獲取msg并且刪除該節(jié)點(diǎn) 
                        if (prevMsg != null) 
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        //返回拿到的消息
                        return msg;
                    }
                } else {
                    //沒(méi)有消息,nextPollTimeoutMillis復(fù)位
                    nextPollTimeoutMillis = -1;
                }
                .....
                .....
              
    }

nativePollOnce()很重要,是一個(gè)native的函數(shù),在native做了大量的工作,主要涉及到epoll機(jī)制的處理(在沒(méi)有消息處理時(shí)阻塞在管道的讀端),具體關(guān)于native相關(guān)的源碼本篇文章不涉及,感興趣的同學(xué)可以網(wǎng)上找找,有不少分析得比較深。

分析到這里,從應(yīng)用啟動(dòng)創(chuàng)建Looper,創(chuàng)建消息隊(duì)列,到進(jìn)入loop方法執(zhí)行無(wú)限循環(huán)中,那么這一塊就告一段落了,主線程已經(jīng)在死循環(huán)里輪詢等待消息了,接下來(lái)我們就要再看看,系統(tǒng)是怎么發(fā)消息給主線程的,主線程是怎么處理這些個(gè)消息的?

在準(zhǔn)備啟動(dòng)一個(gè)Activity的時(shí)候,系統(tǒng)服務(wù)進(jìn)程下的ActivityManagerService(簡(jiǎn)稱AMS)線程會(huì)通過(guò)Binder發(fā)送IPC調(diào)用給APP進(jìn)程,App進(jìn)程接到到調(diào)用后,通過(guò)App進(jìn)程下的Binder線程最終調(diào)用ActivityThread類下面的scheduleLaunchActivity方法來(lái)準(zhǔn)備啟動(dòng)Activity,看下scheduleLaunchActivity方法:

注:Binder線程:具體是指ApplicationThread,在App進(jìn)程中接受系統(tǒng)進(jìn)程傳遞過(guò)來(lái)的信息的線程(在主線程進(jìn)入死循環(huán)之前創(chuàng)建了這個(gè)線程)。

  //這個(gè)方法不是在主線程調(diào)用,是Binder線程下調(diào)用的
  public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
  }

把啟動(dòng)一些信息封裝成ActivityClientRecord之后,最后一句調(diào)用sendMessage(H.LAUNCH_ACTIVITY, r);我們接著往下看:

private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
         Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

看到?jīng)]有,最后啟動(dòng)Activity的信息都封裝一個(gè)Message,但是這里有個(gè)問(wèn)題了,之前在分析main函數(shù)的時(shí)候,完全沒(méi)給出往主線程消息隊(duì)列插入消息的方式,這里有了消息,但是怎么發(fā)到主線程的消息隊(duì)列呢?最后一句又是重點(diǎn)mH.sendMessage(msg); mH 是什么呢?難道是Handler,我們來(lái)看下它是什么東西?
我們看了下ActivityThread 的成員變量,發(fā)現(xiàn)一句初始化的代碼

final H mH = new H();

繼續(xù)往下看H是什么?

public final class ActivityThread{
     ....
     final H mH = new H();
     ....
     private class H extends Handler {
     ....
     ....
     public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                case RELAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                    handleRelaunchActivity(r);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&2) != 0);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                   .....
         }
         .....
         .....
     }
}

H 果不出其然是Handler,而且是ActivityThread的內(nèi)部類,看了一下它的handleMessage 方法,LAUNCH_ACTIVITY、PAUSE_ACTIVITY、RESUME_ACTIVITY...好多好多,H 類幫我們處理了好多聲明周期的事情。那么再回到mH.sendMessage(msg)這句代碼上,在Binder線程執(zhí)行mH.sendMessage(msg);,由主線程創(chuàng)建的Handler mH實(shí)例發(fā)送消息到主線程的消息隊(duì)列里,消息隊(duì)列從無(wú)到有,主線程堵塞被喚醒,主線程loop拿到消息,并回調(diào)mHhandleMessage 方法處理LAUNCH_ACTIVITY 等消息。從而實(shí)現(xiàn)Activity的啟動(dòng)。

講到這里,基本一個(gè)啟動(dòng)流程分析完了,大家可能比較想知道的是 mH.sendMessage(msg); 關(guān)于Hanlder是怎么把消息發(fā)到主線程的消息隊(duì)列的?我們接下來(lái)就講解下Handler,首先看下Handler的源碼!我們先來(lái)看看我們經(jīng)常用的Handler的無(wú)參構(gòu)造函數(shù),實(shí)際調(diào)用的是Handler(Callback callback, boolean async)構(gòu)造函數(shù)(看注釋)

 public Handler() {
        this(null, false);
 }
 public Handler(Callback callback, boolean async) {
        //不是static 發(fā)出可能內(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());
            }
        }
        //獲取當(dāng)前線程的Looper,還記得前面講過(guò) Looper.myLooper()方法了嗎?
        //Looper.myLooper()內(nèi)部實(shí)現(xiàn)可以先簡(jiǎn)單理解成:map.get(Thread.currentThread()) 
        //獲取當(dāng)前線程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            //當(dāng)前線程不是Looper 線程,沒(méi)有調(diào)用Looper.prepare()給線程創(chuàng)建Looper對(duì)象
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //讓Handler 持有當(dāng)前線程消息隊(duì)列的引用
        mQueue = mLooper.mQueue;
        //這些callback先不管,主要用于handler的消息發(fā)送的回調(diào),優(yōu)先級(jí)是比handlerMessage高,但是不常用
        mCallback = callback;
        mAsynchronous = async;
    }

上面的代碼說(shuō)明了下面幾個(gè)問(wèn)題:
1、Handler 對(duì)象在哪個(gè)線程下構(gòu)建(Handler的構(gòu)造函數(shù)在哪個(gè)線程下調(diào)用),那么Handler 就會(huì)持有這個(gè)線程的Looper引用和這個(gè)線程的消息隊(duì)列的引用。因?yàn)槌钟羞@個(gè)線程的消息隊(duì)列的引用,意味著這個(gè)Handler對(duì)象可以在任意其他線程給該線程的消息隊(duì)列添加消息,也意味著Handler的handlerMessage 肯定也是在該線程執(zhí)行的。
2、如果該線程不是Looper線程,在這個(gè)線程new Handler 就會(huì)報(bào)錯(cuò)!
3、上面兩點(diǎn)綜合說(shuō)明了下面一段很常見的代碼:把普通線程轉(zhuǎn)成Looper線程的代碼,為什么在Looper.prepare()Looper.loop()中間要?jiǎng)?chuàng)建Handler:

 class LooperThread extends Thread {
       //其他線程可以通過(guò)mHandler這個(gè)引用給該線程的消息隊(duì)列添加消息
       public Handler mHandler;
       public void run() {
            Looper.prepare();
            //需要在線程進(jìn)入死循環(huán)之前,創(chuàng)建一個(gè)Handler實(shí)例供外界線程給自己發(fā)消息
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    //Handler 對(duì)象在這個(gè)線程構(gòu)建,那么handleMessage的方法就在這個(gè)線程執(zhí)行
                }
            };
            Looper.loop();
        }
    }

那么接下來(lái),我們接著往下看Handler的sendMessage(msg)方法,這個(gè)方法也是比較重要的,也比較常用,Handler 有很多sendXXXX開頭的方法sendMessageAtTime、sendEmptyMessageDelayed、sendEmptyMessage等等,都是用來(lái)給消息隊(duì)列添加消息的,那么這些方法最終都會(huì)調(diào)用enqueueMessage來(lái)實(shí)現(xiàn)消息進(jìn)入隊(duì)列:

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //這句話很重要,讓消息持有當(dāng)前Handler的引用,在消息被Looper線程輪詢到的時(shí)候
        //回調(diào)handler的handleMessage方法
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //調(diào)用MessageQueue 的enqueueMessage 方法把消息放入隊(duì)列
        return queue.MessageQueue(msg, uptimeMillis);
    }

我們?cè)賮?lái)看下MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:

    boolean enqueueMessage(Message msg, long when) {
        // msg 必須有target也就是必須有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.");
        }
        //插入消息隊(duì)列的時(shí)候需要做同步,因?yàn)闀?huì)有多個(gè)線程同時(shí)做往這個(gè)隊(duì)列插入消息
        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();
            //when 表示這個(gè)消息執(zhí)行的時(shí)間,隊(duì)列是按照消息執(zhí)行時(shí)間排序的
            //如果handler 調(diào)用的是postDelay 那么when=SystemClock.uptimeMillis()+delayMillis
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // p==null 表示當(dāng)前消息隊(duì)列沒(méi)有消息
                msg.next = p;
                mMessages = msg;
                //需要喚醒主線程,如果隊(duì)列沒(méi)有元素,主線程會(huì)堵塞在管道的讀端,這時(shí)
                //候隊(duì)列突然有消息了,就會(huì)往管道寫入字符,喚醒主線程
                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;
                //將消息放到隊(duì)列的確切位置,隊(duì)列是按照msg的when 排序的,鏈表操作自己看咯
                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;
            }

            // 如果需要喚醒Looper線程,這里調(diào)用native的方法實(shí)現(xiàn)epoll機(jī)制喚醒線程,我們就不在深入探討了
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

最后我們?cè)倏聪翲andler 的dispatchMessage方法,這個(gè)方法在Looper線程從消息隊(duì)列拿出來(lái)的時(shí)候,通過(guò)msg.target.dispatchMessage(msg)調(diào)用的。

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        //優(yōu)先調(diào)用callback方法
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //最后會(huì)回調(diào)我們重寫的handleMessage 方法
            handleMessage(msg);
        }
    }

到這里,整個(gè)Android的消息處理機(jī)制Java層內(nèi)容基本講解完畢了(歡迎關(guān)注我的簡(jiǎn)書:Kelin)

注:【轉(zhuǎn)載請(qǐng)注明,問(wèn)題可留言,錯(cuò)誤望指出,喜歡可打賞,博客持續(xù)更新,歡迎關(guān)注】

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

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

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