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

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

Android 消息處理機制估計都被寫爛瞭,但是依然還是要寫一下,因為Android應用程序是通過消息來驅動的,Android某種意義上也可以說成是一個以消息驅動的系統,UI、事件、生命周期都和消息處理機制息息相關,並且消息處理機制在整個Android知識體系中也是尤其重要,在太多的源碼分析的文章講得比較繁瑣,很多人對整個消息處理機制依然是懵懵懂懂,這篇文章通過一些問答的模式結合Android主線程(UI線程)的工作原理來講解,源碼註釋很全,還有結合流程圖,如果你對Android 消息處理機制還不是很理解,我相信隻要你靜下心來耐心的看,肯定會有不少的收獲的。

概述

1、我們先說下什麼是Android消息處理機制?

消息處理機制本質:一個線程開啟循環模式持續監聽並依次處理其他線程給它發的消息。

簡單的說:一個線程開啟一個無限循環模式,不斷遍歷自己的消息列表,如果有消息就挨個拿出來做處理,如果列表沒消息,自己就堵塞(相當於wait,讓出cpu資源給其他線程),其他線程如果想讓該線程做什麼事,就往該線程的消息隊列插入消息,該線程會不斷從隊列裡拿出消息做處理。

2、Android消息處理機制的工作原理?

打個比方:公司類比App

PM 的主要工作是設計產品,寫需求文檔,改需求,中途改需求,提測前改需求…UI 主要工作是UI設計,交互等。RD 工作我就不說瞭CEO 不解釋。

公司開創之後(App啟動),那麼CEO開始幹活瞭(主線程【UI線程】啟動),這時候CEO開啟瞭無限循環工作狂模式,自己的公司沒辦法啊(相當於UI主線程轉成Looper線程【源碼裡面有】)CEO招瞭一名RD(new Handler 實例)並把告訴PM和UI,如果你們有什麼任務和需求就讓RD(Handler實例)轉告給我(CEO)。RD會把PM和UI的需求(Message)一條條記到CEO的備忘錄裡(MessageQueue)。CEO 無限循環的工作就是不斷查看備忘錄,看有什麼任務要做,有任務就從備忘錄一條一條拿出任務來,然後交給這一名RD(Handler 實例)去處理(畢竟CEO 不會寫代碼 囧…)。當然如果備忘錄都做完瞭,這時候CEO就會去睡覺(線程堵塞【簡單理解成線程wait】,讓出CPU資源,讓其他線程去執行)。但是這個備忘錄有個特殊的功能就是沒有任務的時候突然插入第一條任務(從無到有)就會有鬧鐘功能叫醒CEO起床繼續處理備忘錄。 整個消息處理機制的工作原理基本就是這樣的。後面會有源碼分析,你再來結合這個場景,會更好理解一些。

這裡先給一張Android消息處理機制流程圖和具體執行動畫,如果看不懂沒事,接著往下看(後面會結合Android UI主線程來講解),然後結合著圖和動畫一塊看更能理解整個機制的實現原理。

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

Looper
我們知道一個線程是一段可執行的代碼,當可執行代碼執行完成後,線程生命周期便會終止,線程就會退出,那麼做為App的主線程,如果代碼段執行完瞭會怎樣?,那麼就會出現App啟動後執行一段代碼後就自動退出瞭,這是很不合理的。所以為瞭防止代碼段被執行完,隻能在代碼中插入一個死循環,那麼代碼就不會被執行完,然後自動退出,怎麼在在代碼中插入一個死循環呢?那麼Looper出現瞭,在主線程中調用Looper.prepare()…Looper.loop()就會變當前線程變成Looper線程(可以先簡單理解:無限循環不退出的線程),Looper.loop()方法裡面有一段死循環的代碼,所以主線程會進入while(true){…}的代碼段跳不出來,但是主線程也不能什麼都不做吧?其實所有做的事情都在while(true){…}裡面做瞭,主線程會在死循環中不斷等其他線程給它發消息(消息包括:Activity啟動,生命周期,更新UI,控件事件等),一有消息就根據消息做相應的處理,Looper的另外一部分工作就是在循環代碼中會不斷從消息隊列挨個拿出消息給主線程處理。

MessageQueue
MessageQueue 存在的原因很簡單,就是同一線程在同一時間隻能處理一個消息,同一線程代碼執行是不具有並發性,所以需要隊列來保存消息和安排每個消息的處理順序。多個其他線程往UI線程發送消息,UI線程必須把這些消息保持到一個列表(它同一時間不能處理那麼多任務),然後挨個拿出來處理,這種設計很簡單,我們平時寫代碼其實也經常這麼做。每一個Looper線程都會維護這樣一個隊列,而且僅此一個,這個隊列的消息隻能由該線程處理。

Handler
簡單說Handler用於同一個進程的線程間通信。Looper讓主線程無限循環地從自己的MessageQueue拿出消息處理,既然這樣我們就知道處理消息肯定是在主線程中處理的,那麼怎樣在其他的線程往主線程的隊列裡放入消息呢?其實很簡單,我們知道在同一進程中線程和線程之間資源是共享的,也就是對於任何變量在任何線程都是可以訪問和修改的,隻要考慮並發性做好同步就行瞭,那麼隻要拿到MessageQueue 的實例,就可以往主線程的MessageQueue放入消息,主線程在輪詢的時候就會在主線程處理這個消息。那麼怎麼拿到主線程 MessageQueue的實例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為瞭統一添加消息和消息的回調處理,又專門構建瞭Handler類,你隻要在主線程構建Handler類,那麼這個Handler實例就獲取主線程MessageQueue實例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的時候就通過這個引用往消息隊列裡插入新消息。Handler 的另外一個作用,就是能統一處理消息的回調。這樣一個Handler發出消息又確保消息處理也是自己來做,這樣的設計非常的贊。具體做法就是在隊列裡面的Message持有Handler的引用(哪個handler 把它放到隊列裡,message就持有瞭這個handler的引用),然後等到主線程輪詢到這個message的時候,就來回調我們經常重寫的Handler的handleMessage(Message msg)方法。

Message
Message 很簡單瞭,你想讓主線程做什麼事,總要告訴它吧,總要傳遞點數據給它吧,Message就是這個載體。

源碼分析

接下來我們會結合App主線程(UI線程)來講解,從App啟動後一步一步往下走分析整個Android的消息處理機制,首先在ActivityThread類有我們熟悉的main的函數,App啟動的代碼的入口就在這裡,UI線程本來隻是一個普通線程,在這裡會把UI線程轉換成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();
        ......
    }
}

首先執行的是 Looper.prepareMainLooper() 我們來看下Looper裡面的這個方法做瞭什麼?

註:看之前先稍微瞭解下ThreadLocal是什麼?
ThreadLocal: 線程本地存儲區(Thread Local Storage,簡稱為TLS),每個線程都有自己的私有的本地存儲區域,不同線程之間彼此不能訪問對方的TLS區域。這裡線程自己的本地存儲區域存放是線程自己的Looper。具體看下ThreadLocal.java 的源碼!

public final class Looper {
    // sThreadLocal 是static的變量,可以先簡單理解它相當於map,key是線程,value是Looper,
    //那麼你隻要用當前的線程就能通過sThreadLocal獲取當前線程所屬的Looper。
    static final ThreadLocal sThreadLocal = new ThreadLocal();
    //主線程(UI線程)的Looper 單獨處理,是static類型的,通過下面的方法getMainLooper() 
    //可以方便的獲取主線程的Looper。
    private static Looper sMainLooper; 

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

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

    private static void prepare(boolean quitAllowed) {
         //如果線程的TLS已有數據,則會拋出異常,一個線程隻能有一個Looper,prepare不能重復調用。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //往線程的TLS插入數據,簡單理解相當於map.put(Thread.currentThread(),new Looper(quitAllowed));
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //實際上是調用  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方法,
        //簡單理解相當於map.get(Thread.currentThread()) 獲取當前線程的Looper
        return sThreadLocal.get();
    }
}

看瞭上面的代碼(仔細看下註釋),我們發現 Looper.prepareMainLooper()做的事件就是new瞭一個Looper實例並放入Looper類下面一個static的ThreadLocal sThreadLocal靜態變量中,同時給sMainLooper賦值,給sMainLooper賦值是為瞭方便通過Looper.getMainLooper()快速獲取主線程的Looper,sMainLooper是主線程的Looper可能獲取會比較頻繁,避免每次都到 sThreadLocal 去查找獲取。

接下來重點是看下Looper的構造函數,看看在new Looper的時候做瞭什麼事?

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

看到沒有,這時候給當前線程創建瞭消息隊列MessageQueue,並且讓Looper持有MessageQueue的引用。執行完Looper.prepareMainLooper() 之後,主線程從普通線程轉成一個Looper線程。目前的主線程線程已經有一個Looper對象和一個消息隊列mQueue,引用關系如下圖:(主線程可以輕松獲取它的Looper,主線程的Looper持有主線程消息隊列的引用)。

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

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

接下來回到ActivityThread 的main函數,執行完Looper.prepareMainLooper() 之後下一句代碼是ActivityThread thread = new ActivityThread();這句話就是創建一下ActivityThread對象,這邊需要註意的時候ActivityThread並不是一個線程,它並沒有繼承Thread,而隻是一個普通的類public final class ActivityThread{…}ActivityThread的構造函數並沒有做什麼事隻是初始化瞭資源管理器。

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

接著往下看下一行代碼

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

thread.attach(false);便會創建一個Binder線程(具體是指ApplicationThread,該Binder線程會通過想 Handler將Message發送給主線程,之後講)。我們之前提到主線程最後會進入無限循環當中,如果沒有在進入死循環之前創建其他線程,那麼待會誰會給主線程發消息呢?,沒錯就是在這裡創建瞭這個線程,這個線程會接收來自系統服務發送來的一些事件封裝瞭Message並發送給主線程,主線程在無限循環中從隊列裡拿到這些消息並處理這些消息。(Binder線程發生的消息包括LAUNCH_ACTIVITY,PAUSE_ACTIVITY 等等)

繼續回到mian 函數的下一句代碼Looper.loop() 那麼重點來瞭,我們來看下Looper.loop()的源碼:

public static void loop() {
    final Looper me = myLooper();  //獲取TLS存儲的Looper對象,獲取當前線程的Looper 
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }

    final MessageQueue queue = me.mQueue;  //獲取Looper對象中的消息隊列
    ....

    for (;;) { //主線程開啟無限循環模式
        Message msg = queue.next(); //獲取隊列中的下一條消息,可能會線程阻塞
        if (msg == null) { //沒有消息,則退出循環,退出消息循環,那麼你的程序也就可以退出瞭
            return;
        }
        ....
        //分發Message,msg.target 是一個Handler對象,哪個Handler把這個Message發到隊列裡,
        //這個Message會持有這個Handler的引用,並放到自己的target變量中,這樣就可以回調我們重寫
        //的handler的handleMessage方法。
        msg.target.dispatchMessage(msg);
        ....
        ....
        msg.recycleUnchecked();  //將Message回收到消息池,下次要用的時候不需要重新創建,obtain()就可以瞭。
    }
}

上面的代碼,大傢具體看下註釋,這時候主線程(UI線程)執行到這一步就進入瞭死循環,不斷地去拿消息隊列裡面的消息出來處理?那麼問題來瞭
1、UI線程一直在這個循環裡跳不出來,主線程不會因為Looper.loop()裡的死循環卡死嗎,那還怎麼執行其他的操作呢?

在looper啟動後,主線程上執行的任何代碼都是被looper從消息隊列裡取出來執行的。也就是說主線程之後都是通過其他線程給它發消息來實現執行其他操作的。生命周期的回調也是如此的,系統服務ActivityManagerService通過Binder發送IPC調用給APP進程,App進程接到到調用後,通過App進程的Binder線程給主線程的消息隊列插入一條消息來實現的。

2、主線程是UI線程和用戶交互的線程,優先級應該很高,主線程的死循環一直運行是不是會特別消耗CPU資源嗎?App進程的其他線程怎麼辦?

這基本是一個類似生產者消費者的模型,簡單說如果在主線程的MessageQueue沒有消息時,就會阻塞在loop的queue.next()方法裡,這時候主線程會釋放CPU資源進入休眠狀態,直到有下個消息進來時候就會喚醒主線程,在2.2 版本以前,這套機制是用我們熟悉的線程的wait和notify 來實現的,之後的版本涉及到Linux pipe/epoll機制,通過往pipe管道寫端寫入數據來喚醒主線程工作。原理類似於I/O,讀寫是堵塞的,不占用CPU資源。

所以上面代碼的重點是queue.next() 的函數,其他的我們就不多說瞭,我們來看下queue.next()的源碼(主要還是看註釋):

Message next() 

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

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

    }

nativePollOnce()很重要,是一個native的函數,在native做瞭大量的工作,主要涉及到epoll機制的處理(在沒有消息處理時阻塞在管道的讀端),具體關於native相關的源碼本篇文章不涉及,感興趣的同學可以網上找找,有不少分析得比較深。

分析到這裡,從應用啟動創建Looper,創建消息隊列,到進入loop方法執行無限循環中,那麼這一塊就告一段落瞭,主線程已經在死循環裡輪詢等待消息瞭,接下來我們就要再看看,系統是怎麼發消息給主線程的,主線程是怎麼處理這些個消息的?

在準備啟動一個Activity的時候,系統服務進程下的ActivityManagerService(簡稱AMS)線程會通過Binder發送IPC調用給APP進程,App進程接到到調用後,通過App進程下的Binder線程最終調用ActivityThread類下面的scheduleLaunchActivity方法來準備啟動Activity,看下scheduleLaunchActivity方法:

註:Binder線程:具體是指ApplicationThread,在App進程中接受系統進程傳遞過來的信息的線程(在主線程進入死循環之前創建瞭這個線程)。

  //這個方法不是在主線程調用,是Binder線程下調用的
  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 pendingResults, List 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);
  }

把啟動一些信息封裝成ActivityClientRecord之後,最後一句調用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);
    }

看到沒有,最後啟動Activity的信息都封裝一個Message,但是這裡有個問題瞭,之前在分析main函數的時候,完全沒給出往主線程消息隊列插入消息的方式,這裡有瞭消息,但是怎麼發到主線程的消息隊列呢?最後一句又是重點mH.sendMessage(msg); mH 是什麼呢?難道是Handler,我們來看下它是什麼東西?
我們看瞭下ActivityThread 的成員變量,發現一句初始化的代碼

final H mH = new H();

繼續往下看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的內部類,看瞭一下它的handleMessage 方法,LAUNCH_ACTIVITY、PAUSE_ACTIVITY、RESUME_ACTIVITY…好多好多,H 類幫我們處理瞭好多聲明周期的事情。那麼再回到mH.sendMessage(msg)這句代碼上,在Binder線程執行mH.sendMessage(msg);,由主線程創建的Handler mH實例發送消息到主線程的消息隊列裡,消息隊列從無到有,主線程堵塞被喚醒,主線程loop拿到消息,並回調mH的handleMessage 方法處理LAUNCH_ACTIVITY 等消息。從而實現Activity的啟動。

講到這裡,基本一個啟動流程分析完瞭,大傢可能比較想知道的是 mH.sendMessage(msg); 關於Hanlder是怎麼把消息發到主線程的消息隊列的?我們接下來就講解下Handler,首先看下Handler的源碼!我們先來看看我們經常用的Handler的無參構造函數,實際調用的是Handler(Callback callback, boolean async)構造函數(看註釋)

 public Handler() {
        this(null, false);
 }
 public Handler(Callback callback, boolean async) {
        //不是static 發出可能內存泄露的警告!
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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,還記得前面講過 Looper.myLooper()方法瞭嗎?
        //Looper.myLooper()內部實現可以先簡單理解成:map.get(Thread.currentThread()) 
        //獲取當前線程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            //當前線程不是Looper 線程,沒有調用Looper.prepare()給線程創建Looper對象
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //讓Handler 持有當前線程消息隊列的引用
        mQueue = mLooper.mQueue;
        //這些callback先不管,主要用於handler的消息發送的回調,優先級是比handlerMessage高,但是不常用
        mCallback = callback;
        mAsynchronous = async;
    }

上面的代碼說明瞭下面幾個問題:
1、Handler 對象在哪個線程下構建(Handler的構造函數在哪個線程下調用),那麼Handler 就會持有這個線程的Looper引用和這個線程的消息隊列的引用。因為持有這個線程的消息隊列的引用,意味著這個Handler對象可以在任意其他線程給該線程的消息隊列添加消息,也意味著Handler的handlerMessage 肯定也是在該線程執行的。
2、如果該線程不是Looper線程,在這個線程new Handler 就會報錯!
3、上面兩點綜合說明瞭下面一段很常見的代碼:把普通線程轉成Looper線程的代碼,為什麼在Looper.prepare()和Looper.loop()中間要創建Handler:

 class LooperThread extends Thread {
       //其他線程可以通過mHandler這個引用給該線程的消息隊列添加消息
       public Handler mHandler;
       public void run() {
            Looper.prepare();
            //需要在線程進入死循環之前,創建一個Handler實例供外界線程給自己發消息
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    //Handler 對象在這個線程構建,那麼handleMessage的方法就在這個線程執行
                }
            };
            Looper.loop();
        }
    }

那麼接下來,我們接著往下看Handler的sendMessage(msg)方法,這個方法也是比較重要的,也比較常用,Handler 有很多sendXXXX開頭的方法sendMessageAtTime、sendEmptyMessageDelayed、sendEmptyMessage等等,都是用來給消息隊列添加消息的,那麼這些方法最終都會調用enqueueMessage來實現消息進入隊列:

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

我們再來看下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.");
        }
        //插入消息隊列的時候需要做同步,因為會有多個線程同時做往這個隊列插入消息
        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 表示這個消息執行的時間,隊列是按照消息執行時間排序的
            //如果handler 調用的是postDelay 那麼when=SystemClock.uptimeMillis()+delayMillis
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // p==null 表示當前消息隊列沒有消息
                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;
                //將消息放到隊列的確切位置,隊列是按照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線程,這裡調用native的方法實現epoll機制喚醒線程,我們就不在深入探討瞭
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

最後我們再看下Handler 的dispatchMessage方法,這個方法在Looper線程從消息隊列拿出來的時候,通過msg.target.dispatchMessage(msg)調用的。

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

到這裡,整個Android的消息處理機制Java層內容基本講解完畢瞭

You May Also Like