Android Handler機制簡單分析

版權說明 : 《Android Handler機制簡單分析》於當前CSDN博客乘月網屬同一原創,轉載請說明出處,謝謝。

本文一切從簡,將圍繞以下流程展開敘述:

這裏寫圖片描述

what?

接觸Android的朋友都知道Handler機制用於多線程方面的通信,這好像是一句廢話。


why?

我們知道java幾個具有代表性的多線程通信方法,例如:

  1. “wait”和”notify”通知機制
    Java中每個類都是Oject的子類(萬物皆對象,滑稽~~),也就具備Oject的”wait()”和”notify()”方法特性。簡單舉例說明:兩個線程中,對於某類的對象a,在線程1中執行a.wait(),線程1則一直處於阻塞中,直到在線程2中執行a.notify(),線程1才被喚醒繼續執行。

  2. “synchronized”線程鎖機制
    多個線程共享一個變量,通過上鎖( synchronized關鍵字 )限制線程們對該變量的訪問,誰拿到鎖,誰便可以對變量進行修改,待其他線程拿到鎖訪問該變量時,根據變量的變化作出相應的處理,以達到通信的目的。

  3. 此處省略n個字…

嗯,上述方法都是利用線程阻塞的方式進行通信。這若在Android中使用?你得先搞清楚3個問題:

  1. Android中多線程通信是爲UI線程(主線程)+Worker線程(子線程)的交互服務的。

  2. 基於問題1,Android的UI線程不允許阻塞,否則會造成”ANR”( 想了解ANR? 傳送門)

  3. 基於問題2,爲避免”ANR”,Android中所有的耗時操作(如網絡請求,文件讀寫)須在子線程中完成,並通知進度或結果給主線程用於UI更新。

綜上:
  既然java原生方法無法滿足Android程序設計方面的要求,那隻能另闢新徑了。還好google比較良心,自己挖“坑”自己補,於是設計了一系列UI線程與Worker線程通信的方法,如:

  • activity.runOnUiThread(Runable action)(Activity類下的切換回UI線程的方法)

  • view.post(Runable action),view.postDelayed(Runnable action, long delayMillis)(View類下的切換回UI線程的方法)

  • 還有本文的主角Handler機制(異步消息處理機制)等等。


how?

先來一段Demo:

......
public class MainActivity extends AppCompatActivity {

    private static final int MSG_DOWNLOAD_TASK = 1;
    private TextView tv_progress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_progress = findViewById(R.id.tv_progress);
    }


    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_DOWNLOAD_TASK:
                    int progress = (int) msg.obj;
                    tv_progress.setText(progress + "");
                    break;
            }
        }
    };

    /**
     * UI上的Button按鈕點擊事件
     * 模擬執行下載任務
     *
     * @param view
     */
    private void download(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int progress = 0;
                try {
                    while (progress >= 100) {
                        Message msg = Message.obtain();
                        msg.what = MSG_DOWNLOAD_TASK;
                        msg.obj = progress;
                        mHandler.sendMessage(msg);

                        /**
                         * 模擬下載進度回調中...
                         */
                        Thread.sleep(1000);
                        progress++;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

上述demo便是Handler的簡單用法,希望大家能看懂。爲了簡練代碼,請忽略內存泄漏~~~


analyze

好了,知道怎麼用了,接下來就得知道爲什麼這樣寫可以切換到主線程,這就麻煩了,得看源碼!!!

怎麼看?直接通過demo看:

1.mHandler = new Handler() {... }初始化Handler

  • 來,我們來看看Handler構造方法在幹嘛:
>>> 下文所有源碼均源於Android8.0,爲了簡練,只保留核心代碼 <<<

    public Handler() {
        this(null, false);//走的是下面的雙參構造方法
    }

    public Handler(Callback callback, boolean async) {
    ......
        mLooper = Looper.myLooper();//把當前線程中Looper對象引用交給Handler
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
                //不能在此線程中創建handler,因爲還沒有調用過Looper.prepare()
        }
        mQueue = mLooper.mQueue;//從Looper對象取出MessageQueue對象給Handler
        mCallback = callback;//null值
        mAsynchronous = async;//false
    }  

  上述代碼,我特意把拋異常的說明翻譯了一下,Excuse me?我並沒有執行啊,怎麼沒報異常?怎麼Looper.myLooper()有值的啊?

  其實這並不矛盾,在同一個線程中可以創建一個或多個Handler對象,但前提必須是當前線程已創建(通過Looper.prepare()創建)並保存或已存在唯一的Looper對象(不理解沒關係,不瞭解Looper也沒有關係,下文會繼續說),Android所有線程之間的通信皆如此,主線程亦然。

  Android中,app運行入口是在ActivityThread類裏的main函數開始的,沒錯,你沒看錯,就是java程序的入口main函數,android app也是java寫的,當然也是main入口的,那麼我們直接看核心源碼來解釋上面的疑問:

......
public final class ActivityThread {
    ......
    public static void main(String[] args) {//app程序入口
        ......
        //1.其實本質還是走Looper.prepare(),見下面Looper類相關代碼便知
        Looper.prepareMainLooper();
        ......
        if (sMainThreadHandler == null) {
            //2.獲取的是Handle子類H對象引用,在H中添加了處理各種消息的業務(不理解沒關係,反正就是創建個Handler子類的對象)
            sMainThreadHandler = thread.getHandler();
        }
        ......
        //3.輪詢消息
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}
  • Looper類下相關代碼:
......
public final class Looper {
......
    public static void prepareMainLooper() {
       //帶參的Looper.prepare(quitAllowed)方法
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {

                throw new IllegalStateException("The main Looper has already been prepared.");
                //已存在Looper對象了,不要再創建了
            }
            sMainLooper = myLooper();
        }
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {

            throw new RuntimeException("Only one Looper may be created per thread");
            //每個線程只能創建一個Looper對象,其實還是在說已存在Looper對象了,不要再創建了
        }
        //這裏創建了久違的Looper對象
        sThreadLocal.set(new Looper(quitAllowed));
    } 

--------------順便看看Looper.prepare()在幹什麼--------------------

    public static void prepare() {
        /**
         *本質是走上面的帶參的prepare(quitAllowed)方法
         *不要太在意quitAllowed參數,反正是傳給Looper對象用的
         */
        prepare(true);
    }
}    

--------------再來看Looper的初始化--------------------

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//傳說中的MessageQueue(消息隊列)對象是在這裏創建的
        mThread = Thread.currentThread();//獲取當前線程對象
    }

小結:
  1.由於是app程序入口,main函數一定執行在主線程(UI線程)上,並且程序一開始就爲主線程創建並保存好了Looper對象,以便爲Handler子類H提供服務,既然已存在,當然不需要自行“Looper.prepare()”了。
  2.Android官方已經爲我們提供了Handler機制代碼模版↓↓↓
Handler流程
邏輯代碼寫法流程圖:
這裏寫圖片描述
  所以,可以這樣歸納:主線程與子線程間通信不需要寫Looper.prepare(…)和Looper.loop(),子線程與主線程以及子線程與子線程間的通信則需要
  3.MessageQueue(消息隊列)對象是在Looper初始化的時候被創建,且一個線程中僅能創建一個Looper對象,所以一個線程中MessageQueue與Looper對象是一對一的關係。

2. Message msg = Message.obtain();子線程中發消息前創建Message對象

  • 先簡單分析下Message.obtain()源碼:
    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     *個人翻譯:從全局池返回一個新Message實例(可能是新創建的,也可能是從全局池中重新複用的)。
     *允許我們在多數情況下避免分配(創建)過多的新對象
     */
    public static Message obtain() {
        synchronized (sPoolSync) {//同步鎖訪問機制
            if (sPool != null) {//池不爲null,複用已存在的對象
                Message m = sPool;//從池中取出Message對象(很明顯這個池也是Message類的對象)
                sPool = m.next;
                /**
                 *結合上面可以知道這個池其實是由多個Message對象組成的鏈表結構(不知道鏈表?找度娘...)
                 *每次複用的都是表頭的Message對象
                 *表頭被取走(複用)後,緊連着表頭的另一個Message對象成爲新的表頭,以此類推
                 *先不要想這個鏈表是怎麼添加Message對象的,也不要着急看Message類全部源碼,因爲不是本文重點
                 */
                m.next = null;//對即將複用的表頭(Message對象)進行脫鏈,從此自由啦!
                m.flags = 0; //clear in-use flag (清除“在使用中的”的標記,恢復初始狀態以便複用)
                sPoolSize--;//複用後,鏈表長度減1
                return m;//返回表頭(複用表頭)
            }
        }
        return new Message();//池爲null時直接創建新Message對象
    }
  • 下面介紹Message的flags屬性:
    /**
     * If set message is in use.
     * This flag is set when the message is enqueued and remains set while it
     * is delivered and afterwards when it is recycled.  The flag is only cleared
     * when a new message is created or obtained since that is the only time that
     * applications are allowed to modify the contents of the message.
     *
     * It is an error to attempt to enqueue or recycle a message that is already in use.
     *
     *個人翻譯:這個值表示message在使用中(即:flags=FLAG_IN_USE=1,不是賦值號!!!)。當消息排隊時設
     *置爲該標誌(設置爲"使用中"狀態),並且在傳送消息過程中保持該狀態,直到之後被回收。
     *只有在新創建(“new Message()”)或獲取(“Message.obtain()”)一個消息時纔會清除該標誌,
     *這是允許應用程序修改消息內容的唯一時間。
     *
     *當某消息處於使用中狀態時,嘗試去排隊或回收該消息是錯誤的。
     *
     *1 << 0還是等於1,不知道谷歌爲啥在很多源碼中都有這種騷操作,如果你知道請下方留言告知,萬分感謝!
     */
    /*package*/ static final int FLAG_IN_USE = 1 << 0; 

    //使用狀態標識,默認爲0,即爲未使用
    /*package*/ int flags;
  • 再看new Message()構造方法源碼:

    /**
     * Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
     * 個人翻譯:構造器(但是推薦的方式是調用"Message.obtain()")
     */
    public Message() {
    }

     嗯哼,如此簡單明瞭的告訴你:其實我的構造方法沒啥騷操作,但希望你優先使用Message.obtain()方式獲取Message實例,避免鋪張浪費。

小結
  關於Message對象的獲取,優先考慮全局池(Message鏈表),有則取表頭並作脫鏈(next= null)和清除”in use”狀態(flags=0)的重置操作,無則“new”一個新對象,此時其flags默認值爲0,next爲null。這與上述翻譯的“清除‘in use’狀態的唯一時間”相對應。

下面是獲取Message對象流程圖
這裏寫圖片描述

3.msg.what = MSG_DOWNLOAD_TASK......mHandler.sendMessage(msg);發送消息

  • 其實對於Message的what和obj用法大家應該很熟悉了,這裏就順便看一下源碼的解釋:
......
    /**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     *個人翻譯:用戶自定義的消息碼,便於接收者(Handler)識別是關於什麼的消息。
     *每個Handler都有自己消息碼命名空間,所以你不用擔心與其他Handler衝突
     *個人解釋:這是一個消息標識,隨便你怎麼定義這個消息碼的值,不用擔心因與其他Handler
     *的Message消息碼相同而衝突,因爲Message對象由哪個handler對象發送,就由那個handler
     *的handleMessage方法接收該消息(不明白?繼續看下文)
     */
    public int what;

    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     *
     * Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     *個人翻譯:一個任意對象發送給接收者。如果它是Parcelable實現類,使用Messenger
     *跨進程(注意是"跨進程"哦)發送消息,只能是非空的。其它數據使用setData方法傳輸
     *個人解釋:可以傳遞是任何Object類型對象,對於Messenger跨進程不是本文重點,請忽略。對於setData
     *方法,方法全名爲setData(Bundle data),表示可以傳一個Bundle類型數據消息
     */
    public Object obj;

    ......
  • 接下來再回看Handler源碼:
......
     /**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     * 個人翻譯:推送一條消息到消息隊列,在所有此前處於等待中的消息之後排隊等待接收。這個消息
     *將由綁定於當前handler對象的線程中被其handleMessage方法接收。
     *個人解釋:當前時間發送的消息,會按先來後到的順序排隊等待被handler的handleMessage
     *方法在handler被創建的線程中接收,好像還是很茫然哈,很正常,往後面看幾段你就明白了)
     *
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     個人翻譯:如果成功放入消息隊列返回true,如果失敗返回false,通常是這個消息隊列被輪詢的looper
     *退出輪詢導致的
     */
    public final boolean sendMessage(Message msg)
    {
        //走下面的方法。前面的方法介紹的很詳細,下面的方法就簡單介紹了
        return sendMessageDelayed(msg,0);
    }

    /**
     *顧名思義,就是延遲delayMillis毫秒後發送消息
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        /**
         *繼續走下面的方法,“SystemClock.uptimeMillis()”表示從系統開機到現在的毫秒數,
         *類似於“System.currentTimeMillis()”,但不完全相同,這個不用糾結,反正就是表達“當前時間”。
         *那麼“+ delayMillis”就是表示從當前時間開始向後延遲delayMillis毫秒
         */
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    /**
     *同樣顧名思義,在指定的時間點開始發送消息,即指定從系統開機時間到uptimeMillis毫秒時開始發送消息
     *貫穿上面的解釋很好理解,如果uptimeMillis=SystemClock.uptimeMillis()就是從此時開始,
     *如果uptimeMillis=SystemClock.uptimeMillis()+delayMillis就是延後delayMillis毫秒開始。
     */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //通過上文可以知道這個消息隊列對象“mQueue”是在Handler初始化時由looper對象賦值給handler的
        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);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //當前Handler對象賦值給msg.target,小彩蛋:爲了之後告訴looper自己(msg)是被哪個handler對象發送的
        msg.target = this;

        /**
         *是否爲異步傳輸,這個會打亂排隊順序(那排隊還有啥用?),所以不推薦使用
         *並且通過上文可知Handler初始化時默認mAsynchronous爲false,所以這段代碼請忽略
         */
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //哎,沒完沒了了,繼續走MessageQueue的enqueueMessage方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }
......
  • 看MessageQueue源碼:
......
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//結合上文可知msg.target不爲null
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {//判斷Message是否爲"in use"狀態,結合上文可知爲0,即非使用狀態
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {//mQuitting默認爲false,只有調用quit()方法纔會爲true,先不考慮這裏
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();//沒錯,這裏就是Message回收到sPool的方法,不是本文重點,有興趣的可以看下
                return false;//還記得sendMessage上的翻譯麼?"通常是這個消息...導致的"。說的就是這裏
            }

            msg.markInUse();//msg.flags設爲FLAG_IN_USE,即進入使用中狀態
            msg.when = when;
            /**
             *這個mMessages便是真正的消息隊列實現者,其本質跟sPool一樣都是Message鏈表,
             *且表頭也是優先級最高的,mMessages默認不會初始化,即mMessages==null
             */
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                //新的表頭,如果阻塞,喚醒事件隊列。
                /**
                 *新的表頭?三個任意條件:mMessages==null,即隊列還不存在,當前來排隊的msg有幸成爲
                 *第一個排隊的,當然是表頭;when == 0,剛開機就發消息?優先級很高啊,當然放在表頭
                 *(雖然不太現實);when < p.when,比鏈表的表頭時間還小,肯定優先發送,當然要放在表
                 頭。
                 */
                msg.next = p;
                mMessages = msg;//msg正式成爲表頭
                ......
            } else {
                ......

                /**
                 *重新組構鏈表,先按時間從小到大的順序排列,如果遇到時間點相同的msg則繼續按先來
                 *後到的順序排列
                 *小插曲:"先來後到"?是不是覺得很眼熟?上文"sendMessage"註釋表達的意思就是下方代
                 *碼的最終實現邏輯。
                 */
                   Message prev;
                for (;;) {//從頭到尾地拆鏈表,爲尋找msg合適的插入位置
                    prev = p;
                    p = p.next;//上下兩句代碼是在拆鏈
                    if (p == null || when < p.when) {
                    //p爲null表示爲表尾了,沒必要繼續拆了
                    //when < p.when表示當前msg已在排到最合適的位置了
                        break;
                    }
                    ......
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg; //將msg插入鏈表中
            }

            ......
        }
        return true;
    }
......

小結:
  因爲handler發送消息最終走的是sendMessageAtTime()方法,所以enqueueMessage()方法下的when其實是指時間點。在若干線程中,任意時間發送多個消息,如果最終調用enqueueMessage時傳入的when(即uptimeMillis
)值都相同,則它們被接收(處理)的時間點相同。上文谷歌在sendMessage()的註釋中提到”當前時間”是指調用sendMessage時,傳入的when,即”SystemClock.uptimeMillis() + delayMillis”與消息隊列中已有的某些msgwhen值相同,需要按先來後到的順序排到這些msg的最後。

下面是消息隊列示意圖:
這裏寫圖片描述

  嗯哼,既然排好隊了,那是不是就等着Looper來輪詢了?Demo沒有給出輪詢代碼,因爲UI線程爲我們寫好了,你懂的。 接下來看analyze收尾篇↓↓↓

4.Looper.loop();輪詢消息

  • 看 “Looper.loop()” 源碼:
......
    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     *個人翻譯:在此線程中運行消息隊列。 請務必調用 quit( )方法以結束循環。
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            //沒有Looper對象;"Looper.prepare()"未在此線程中調用。
            //這個不用多說了吧?
        }
        final MessageQueue queue = me.mQueue;//獲取MessageQueue對象
        ......
        for (;;) {//輪詢
            //1.從消息隊列裏取消息
            Message msg = queue.next(); // might block 可能會阻塞 
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                //沒有消息表明消息隊列正在退出。
                return;//退出輪詢
            }

            ......
            try {
                msg.target.dispatchMessage(msg);//2.分發消息(交由與之關聯的handler接收處理)
                ......
            } finally {
                ......
            }
            ......
            msg.recycleUnchecked();//3.消息處理結束,直接回收到全局池sPool
        }
    }
......
  • 看看 “queue.next()” 獲取消息流程:
    Message next() {
        ......
        /**
         * 線程阻塞的時間
         *-1:一直阻塞,直到線程被喚醒爲止。如果期間有程序喚醒線程會立即向下執行。比如新消息進入隊列觸發喚醒
         * 0:不阻塞
         *>0: 最長阻塞nextPollTimeoutMillis毫秒。如果期間有程序喚醒線程會立即向下執行。比如新消息進入隊列觸發喚醒
         */
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {//需要阻塞
                //在底層做好阻塞線程相關準備,主要是釋放已掛起的對象
                Binder.flushPendingCommands();
            }
            //阻塞線程
            nativePollOnce(ptr, nextPollTimeoutMillis);

            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) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        //下一條消息沒有準備好。 設置超時以在準備就緒時喚醒。
                        //消息接收處理的時間還沒到,計算休眠時間,以便下次來判斷此消息是否可以執行
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        //獲取一個msg
                        mBlocked = false;
                        if (prevMsg != null) {//不爲null屬於異步範圍,本文不考慮
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;//對msg(表頭)作脫鏈處理
                        }
                        msg.next = null;//msg徹底解放出來了
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();//標記爲使用中
                        return msg;//返回msg
                    }
                } else {
                    // No more messages.
                    //沒有更多的消息了,讓線程一直阻塞,直到線程被喚醒(一般有新消息進入隊列會直接觸發喚醒)
                    nextPollTimeoutMillis = -1;
                }
                // Process the quit message now that all pending messages have been handled.
                // 處理完所有待處理消息後,立即退出輪詢。
                if (mQuitting) {//消息隊列正在退出
                    dispose();//native底層註銷和釋放資源,完成退出
                    return null;
                }                

                ......//此處忽略了執行IdleHandler代碼 
        }
    }
  • 再看”msg.target.dispatchMessage(msg)”在幹什麼。高能預警↓↓↓
    /**
     * Handle system messages here.
     *在這裏處理系統消息。
     */
    public void dispatchMessage(Message msg) {

        if (msg.callback != null) {
            /**
             * callback!=null表示通過postXXX()方法發送的消息,不需要走handleMessage方法
             * 如:new Handler().postDelayed(Runnable r, long delayMillis);
             */
            handleCallback(msg);
        } else {
            /**mCallback!=null表示創建Handler時直接傳入CallBack實現
             *類,直接調用CallBack的handleMessage方法就好了
             *如:new Handler(Callback callback);
             */
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            /**
             *嗯哼,沒有傳callback的話,那就走Handler的handleMessage()方法渠道咯
             *這也正是我們Demo所走的渠道,終於等到你,還好我沒放棄,額額額...
             */
            handleMessage(msg);
        }
    }

小結:
  “Looper.loop()”依賴兩個for循環來維持消息輪詢和分發,外環重複着三大任務:1.獲取消息(queue.next())。2.分發消息(msg.target.dispatchMessage(msg))。3.回收消息(msg.recycleUnchecked())。
  外環由msg==null條件成立而終止,爲了讓輪詢一直維持下去,queue.next()作爲內環既要承擔這個任務,也要篩選msg提供給外環分發:1.有合適的msg則返回給外環。2.有消息但沒到分發時間點,則阻塞線程,最長阻塞nextPollTimeoutMillis毫秒喚醒,期間可能被一些因素喚醒,如有新消息進入隊列。3.無消息,即mMessages==null,則一直阻塞線程,期間可能被一些因素喚醒,如有新消息進入隊列。4.如果消息隊列退出,即mQuitting==true,則返回null,此時外環因msg==null而終止。
  可能還有人在疑問:哪裏能看得出handleMessage()已經切換到目標線程了?這個問題我還真被人問過,這裏順便回答一下:因爲”handleMessage()”在”dispatchMessage()”下執行,而”dispatchMessage()”又在”loop()”下執行,”loop()”本身就運行在目標線程,這樣夠清晰了嗎?嗯?
  
  至此,關於Handler機制的分析就告一段落了,寫作期間因爲各種原因中斷了很多次,也隔了很久,導致思路對接不通,不清晰,望請原諒,後期會不斷優化更新~~

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