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、LooperHandlerMessageQueue,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<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //主線程(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<Looper> 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線程會通過想 HandlerMessage發送給主線程,之後講)。我們之前提到主線程最後會進入無限循環當中,如果沒有在進入死循環之前創建其他線程,那麼待會誰會給主線程發消息呢?,沒錯就是在這裏創建了這個線程,這個線程會接收來自系統服務發送來的一些事件封裝了Message併發送給主線程,主線程在無限循環中從隊列裏拿到這些消息並處理這些消息。(Binder線程發生的消息包括LAUNCH_ACTIVITYPAUSE_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<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);
  }

把啓動一些信息封裝成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_ACTIVITYPAUSE_ACTIVITYRESUME_ACTIVITY...好多好多,H 類幫我們處理了好多聲明週期的事情。那麼再回到mH.sendMessage(msg)這句代碼上,在Binder線程執行mH.sendMessage(msg);,由主線程創建的Handler mH實例發送消息到主線程的消息隊列裏,消息隊列從無到有,主線程堵塞被喚醒,主線程loop拿到消息,並回調mHhandleMessage 方法處理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<? 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,還記得前面講過 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開頭的方法sendMessageAtTimesendEmptyMessageDelayedsendEmptyMessage等等,都是用來給消息隊列添加消息的,那麼這些方法最終都會調用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層內容基本講解完畢了(歡迎關注我的簡書:Kelin)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章