Android源碼學(xué)習(xí)--Android Handler 消息機(jī)制

Handler 在開發(fā)中使用的非常平凡,它基于一種隊(duì)列消息機(jī)制,循環(huán)處理消息或者開發(fā)者的代碼任務(wù),實(shí)現(xiàn)不同線程之間的通信,下面結(jié)合源碼學(xué)習(xí)其如何實(shí)現(xiàn)。

  • 主線程如何建立Handler消息機(jī)制

當(dāng)我們點(diǎn)擊launch的app圖標(biāo)時(shí),系統(tǒng)會做什么,系統(tǒng)肯定會首先創(chuàng)建一個(gè)進(jìn)程,為該app獨(dú)有,接著,肯定是主線程了,也就是平常經(jīng)常說的UI線程,不能執(zhí)行耗時(shí)操作...,根據(jù)java的特性,肯定會執(zhí)行一個(gè)類的main方法,這里,系統(tǒng)執(zhí)行了ActivityThread的main方法。
點(diǎn)開ActivityThread類,main如下(代碼太長,省略部分):

ActivityThread:
    public static void main(String[] args) {
    ...

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

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

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
      ...
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
   }

首先調(diào)用了Looper的prepareMainLooper(),所以開發(fā)者就不需要在主線程調(diào)用Looper.prepare()直接使用handle,該靜態(tài)方法內(nèi)容如下:

    public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
    }

    //對開發(fā)者提供的prepare(),默認(rèn)傳true,下面會分析
    public static void prepare() {
        prepare(true);
      }
 
    private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

和咱調(diào)用Looper一樣,主線程的Looper也是得先prepare,但傳入了false值,深入prepare()源碼,可以發(fā)現(xiàn),該false值最終傳入了Looper對象所維護(hù)的MessageQueue中的mQuitAllowed變量中,而在MessageQueue中作用是什么呢,ctrl+f,查找mQuitAllowed使用過的地方...發(fā)現(xiàn)僅在下面這些地方出現(xiàn)過:

MessageQueue:
void quit(boolean safe) {
     if (!mQuitAllowed) {
              throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }
        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

噢了,用來防止開發(fā)者終止主線程的消息隊(duì)列的,停止Looper循環(huán)的。繼續(xù)Looper的prepareMainLooper(),在Looper prepare完后,就是創(chuàng)建Looper對象了,并且存在了Looper靜態(tài)變量中。這樣主線程的Looper就算準(zhǔn)備完畢,Looper的消息隊(duì)列MessageQueue也創(chuàng)建處理,作為傳送帶的角色隨時(shí)待命開機(jī)傳送...

再回到ActivityThread的main方法,在 Looper.prepareMainLooper()執(zhí)行完畢后,就輪到Handler處理者角色創(chuàng)建啦,作為一套成型的消息處理機(jī)制,Handler作為消息的消費(fèi)者,地位肯定不可或缺。

再分析下main方法,在執(zhí)行 sMainThreadHandler = thread.getHandler();之前,主線程創(chuàng)建了ActivityThread對象并調(diào)用了attach函數(shù),這里干嘛吶,這里就涉及到應(yīng)用的初始化啦,比如Application創(chuàng)建,Activity創(chuàng)建,執(zhí)行應(yīng)用的生命周期啊等等,挺復(fù)雜,還沒轉(zhuǎn)過彎...反正里面的操作也是向主線程消息隊(duì)列發(fā)送消息的。

但是這里可以想下,既然這里send了Message,但Looper還沒有開始啟動MessageQueue循環(huán)處理啊,有用嗎?這里再一步步深入Handler源碼的sendMessage方法,關(guān)鍵代碼如下:

Handle:
  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
      if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
  }
MessageQueue:
  boolean enqueueMessage(Message msg, long when) {
        ...
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            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;
            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;
        } 
        ...  
    return true;
}

可以看出,Handle的sendMessage是將Message send進(jìn)了MessageQueue,enqueueMessage()方法中就是執(zhí)行隊(duì)列的操作,將Message加入一個(gè)隊(duì)列中了,就相當(dāng)于一個(gè)個(gè)的放上傳送帶了并且通過next引用互相聯(lián)系著,通過for(;;)循環(huán),找到隊(duì)列的最后個(gè)Message,然后將最后一個(gè)的Message的next指針指向新來的msg,鏈表結(jié)構(gòu)哈。

這樣就清楚上面的疑惑了,傳送帶還沒開動,但先把物品給放上去排好,待會跑起來了,再一個(gè)個(gè)給處理,一點(diǎn)不耽誤。

啥時(shí)候跑起來呢,繼續(xù)擼咱的main方法,好了,說曹操曹操就到,接下來就是 Looper.loop();傳送帶開關(guān)開了,老司機(jī)開車了,Looper.loop()源碼:

Looper:
    /**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    
    //  開始在當(dāng)前線程中循環(huán)...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }

為什么要循環(huán)起來呢,因?yàn)榫€程的生命周期限制于它的有效代碼行數(shù),循環(huán)起來,有效代碼相對增多了,生命周期就延長了,這也保證了app能一直運(yùn)行了,這也是主線程的Looper調(diào)用quit()會拋異常的原因,主線程quit了app還怎么玩...

開始 loop之前,要獲取當(dāng)前線程持有的Looper對象,有嗎,沒有就果斷拋異常,有?拿出Looper的MessageQueue傳送帶來,接下來for循環(huán)開啟傳送帶開關(guān),運(yùn)轉(zhuǎn)起來了。

這里為啥用for(;;)而不是while呢,有說習(xí)慣問題,有說防止被修改字節(jié)碼,也有人說比while少個(gè)判斷,具體也是不清楚,還請高人指點(diǎn)...

接下來循環(huán)體就直截了當(dāng)了,MessageQueue的next是阻塞方法,在這里等待Handle send的Message,來一個(gè)message,就放行一次,然后從message中獲取Handler引用,讓handle dispatchMessage,dispatchMessage也不復(fù)雜:

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

先判斷有沒有Runnable類型消息,有就執(zhí)行run回調(diào)唄,沒有就走實(shí)例化Handler時(shí)重寫的handleMessage,里面就根據(jù)Message類型及參數(shù)來執(zhí)行不同邏輯。

Handle消息處理流程圖

到這里就建立好了主線程的消息處理機(jī)制,應(yīng)用層開發(fā)者直接通過實(shí)例化Handle sendMessage或者postRunnable就可以向主線程發(fā)送消息了。

  • 主線程不能有多余5秒的耗時(shí)操作

都知道主線程中不能有超過5秒的無響應(yīng)操作,否者就ANR(Application Not Responding)了,結(jié)合上面loop()源碼的分析,既然主線程一直阻塞在等待send的message,那么,計(jì)時(shí)系統(tǒng)應(yīng)該在msg.target.dispatchMessage(msg); 處理message方法的前后加的,在執(zhí)行前,開始計(jì)時(shí),當(dāng)達(dá)到5秒時(shí),跳出ANR異常,所有,初步猜想可能和Trace這個(gè)類有關(guān),還請指點(diǎn)!

  • 子線程創(chuàng)建Handler消息機(jī)制

子線程創(chuàng)建Handler消息機(jī)制,步驟和主線程的一樣,先Looper.prepare(),來為當(dāng)前線程綁定新的Looper對象,同時(shí),Looper對象會實(shí)例化持有的MessageQueue,然后就可以通過實(shí)例化Handler發(fā)送Message了,但這時(shí)發(fā)了是處理不了的,Looper需要通過loop循環(huán)當(dāng)前的MessageQueue,一個(gè)個(gè)取Message。
所有,子線程有上面操作需要注意啦,loop()之后,子線程生命周期就延長了,除非調(diào)用Looper.quit(),就結(jié)束循環(huán)了。

  • 為何子線程中直接使用Toast報(bào)錯(cuò)

翻看Toast源碼,可以看到Toast底層也是用了Handler機(jī)制發(fā)送消息,這就不難理解了,Handler消息機(jī)制需要依附于當(dāng)前線程的Looper對象來運(yùn)作,子線程沒有自己的Looper,Handler也就使用報(bào)錯(cuò),Toast也就不能用啦。
具體參看:http://blog.csdn.net/saroll57/article/details/23276275

  • 為何UI操作要在主線程中

UI操作不是線程安全的,比如View的invalidate()就是非線程安全的,View的各種操作最終都會導(dǎo)致invalidate()函數(shù)執(zhí)行,多個(gè)線程執(zhí)行同一UI操作就會導(dǎo)致同時(shí)有多次刷新,就會出現(xiàn)異常情況。

子線程操作UI,可以通過獲取主線程handler的引用發(fā)送message來實(shí)現(xiàn),或者通過:

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

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