android handler機制源碼解析

android handler、looper異步消息處理機制

可能萌新感覺異步消息處理這個東西很高端,其實我們平時一直在用,最常用的該是子線程通知ui線程刷新了吧。sendMessage、handleMessage是不是突然間感覺就來了。
異步消息處理當(dāng)然離不開handler、looper、message以及messageQueue、threadLocal.....

那么先從handler開始

handler的主要作用其實就是sendMessage handleMessage。不過呢發(fā)送msg有好幾個函數(shù)(androidStuido輸入handler.send會彈出好幾個方法),最后基本上都是走這個關(guān)鍵的函數(shù)

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

可以看到最后是要enqueueMessage(queue, msg, uptimeMillis);的,可以理解為把這次handler發(fā)送的msg發(fā)到對應(yīng)的queue(messageQueue)里面,延遲時間為uptimeMillis。

說到messageQueue,要有這個意識(暫且記住,后面會詳解)

  • handler必須持有一個looper,這個是在構(gòu)造handler的時候就會拿到的
    不管是否是ui線程的mainLooper,handler使用的時候一定要有l(wèi)ooper,比如Toast在子線程使用時可以不使用ui線程的mainLooper,但是必須有一個looper,不然會拋出異常。

  • looper和一個massageQueue對應(yīng),并且和一個線程對應(yīng)(注意了啊和線程相關(guān)的來了),一個線程最多有一個looper

由上面三條我們暫且可以得到這個信息:在創(chuàng)建handler的線程會綁定一個looper,既然有l(wèi)ooper肯定有一個專門的messageQueue.那么假如我在子線程Anew了一個handler(前提是該線程要prepare一個looper),那么這個子線程A就會有一個在該線程一直循環(huán)的looper來處理該線程messageQueue里面的msg。
然后如果我在另外一個線程B獲取到了該handler的對象,然后發(fā)送一個msg,因為handler、looper、messageQueue是對應(yīng)的,所以發(fā)送的msg最后是入隊到了創(chuàng)建該handler線程A的messageQueue上。已經(jīng)切到A線程就很簡單了啊,queue里面的msg取出來處理就完了。

好,現(xiàn)在我們已經(jīng)發(fā)送msg,并且送到對應(yīng)的queue里面了。接下來,Looper會一直循環(huán)的詢問它的messageQueue是否來消息了,一旦來了就會把msg取出來,然后讓它對應(yīng)的handler.dispatchMessage來分發(fā)這個消息。
直接上源碼:

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

可以看到我們經(jīng)常用的handleMessage(msg)是在最后,需要前面的都放棄,才會執(zhí)行到它。那么前面的msg.callback以及mCallback是什么呢?

先說msg.callback

查看Message源碼發(fā)現(xiàn)msg.callback實際上是一個Runnable;不知道大家除了handler.sendMessage 以及handleMessage之外 handler.post(Runnable)、view.post(Runnable)、runOnUiThread(Runnable)有沒有使用過;這些都是可以實現(xiàn)在子線程通知ui線程進行更新UI操作。
這里面的Runnable參數(shù)就是msg.callback。其實上面3種方式到后面都調(diào)用了handler.post,那我們就看一下這個是干什么的

 public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

可以看到把runnable設(shè)置給msg后,到后面又是sendMessageAtTime(不記得的往上翻)。
所以啊,如果以上面三種方式,handler.handleMessage是不會觸發(fā)的哦,因為根本就沒走到那里,而是走了handleCallback(msg);`

private static void handleCallback(Message message) {
        message.callback.run();
    }

請牢記,通過handler把消息放到了對應(yīng)的msgQueue里面的時候,因為msgQueue和Looper是綁定的,所以等到looper用對應(yīng)的handler dispatch消息的時候,已經(jīng)切到了創(chuàng)建looper的線程了。

再簡單說一下mCallback

這其實是一個回調(diào)接口,創(chuàng)建handler的時候可以傳幾個參數(shù):looper、mcallBack、flag。平時我們什么默認值,其中mcallBack=Null;當(dāng)然也可以實現(xiàn)這個回調(diào)接口,重寫回方法

像這樣:

handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                Log.v("tag","uiThread 接口回調(diào) :"+Thread.currentThread().getId());
                return true;
            }
        }){
            @Override
            public void handleMessage(Message msg) {
//                super.handleMessage(msg);
                Log.v("tag","uiThread 重寫handleMessage:"+Thread.currentTead().getId());
       

     }
        };

再看上面dispatchMessage方法回調(diào)方法返回false則會繼續(xù)執(zhí)行到handler.handlerMessage();返回true就直接return了。

現(xiàn)在我們對handler的發(fā)消息和處理消息應(yīng)該有比較清晰的認識了。只是還需合Looper來看。提到一個looper對應(yīng)一個線程那一定要看ThreadLocal。 這些下次再詳細講解。
上面提到的幾種發(fā)送消息的方式基本上最后都是執(zhí)nqueuessage(queue, msg, 0);是因為有一種方式是sendMessageAtFrontOfQueue(Message msg)它會直接到消息入隊那里 enqueueMessage(queue, msg, 0);

題外話:源碼什么的,不懂就反復(fù)看;能點進去就帶著好奇心點進去看看,如果是紅的,點不動到frameWork了,就暫時不要導(dǎo)入frameWork源碼深入了,這個有點多。如果最后點進去是native方法,涉及到JNI了,也暫停。等后面熟悉了sdk的源碼再接觸frameWork和jni。

我的簡書地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容