Android 消息循環機制源碼分析

概述

搞Android的不懂Handler消息循環機制,都不好意思說自己是Android工程師。面試的時候一般也都會問這個知識點,但是我相信大多數碼農肯定是沒有看過相關源碼的,頂多也就是網上搜搜,看看別人的文章介紹。學姐不想把那個萬能的關係圖拿出來討論。

學姐先從我們平時的使用方法引出這個機制,再結合源碼進行分析。

我們平時使用是這樣的:

    //1. 主線程
   
Handler handler = new MyHandler();

   
//2. 非主線程
   
HandlerThread handlerThread = new HandlerThread("handlerThread");
    handlerThread
.start();
   
Handler handler = new Handler(handlerThread.getLooper());

   
//發送消息
    handler
.sendMessage(msg);
   
   
//接收消息
   
static class MyHandler extends Handler {
       
//對於非主線程處理消息需要傳Looper,主線程有默認的sMainLooper
       
public MyHandler(Looper looper) {
           
super(looper);
       
}

       
@Override
       
public void handleMessage(Message msg) {
           
super.handleMessage(msg);
       
}
   
}

那麼爲什麼初始化的時候,我們執行了1或2,後面只需要sendMessage就可處理任務了呢?學姐這裏以非主線程爲例進行介紹,handlerThread.start()的時候,實際上創建了一個用於消息循環的Looper和消息隊列MessageQueue,同時啓動了消息循環,並將這個循環傳給Handler,這個循環會從MessageQueue中依次取任務出來執行。用戶若要執行某項任務,只需要調用handler.sendMessage即可,這裏做的事情是將消息添加到MessaeQueue中。對於主線程也類似,只是主線程sMainThread和sMainLooper不需要我們主動去創建,程序啓動的時候Application就創建好了,我們只需要創建Handler即可。

我們這裏提到了幾個概念:

HandlerThread 支持消息循環的線程

Handler 消息處理器

Looper 消息循環對象

MessageQueue 消息隊列

Message 消息體

對應關係是:一對多,即(一個)HandlerThread、Looper、MessageQueue -> (多個)Handler、Message

class="wp-embedded-content" sandbox="allow-scripts" security="restricted" src="http://w3cschool.codecloud.net/android-training/index.html/embed#?secret=T759duF3w0" data-secret="T759duF3w0" width="500" height="461" title="嵌入的WordPress文章" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="box-sizing: border-box;">

源碼解析

1. Looper

(1)創建消息循環

prepare()用於創建Looper消息循環對象。Looper對象通過一個成員變量ThreadLocal進行保存。

(2)獲取消息循環對象

myLooper()用於獲取當前消息循環對象。Looper對象從成員變量ThreadLocal中獲取。

(3)開始消息循環

loop()開始消息循環。循環過程如下:

每次從消息隊列MessageQueue中取出一個Message

使用Message對應的Handler處理Message

已處理的Message加到本地消息池,循環複用

循環以上步驟,若沒有消息表明消息隊列停止,退出循環

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

public static Looper myLooper() {
   
return sThreadLocal.get();
}

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

   
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
       
Printer logging = me.mLogging;
       
if (logging != null) {
            logging
.println(">>>>> Dispatching to " + msg.target + " " +
                    msg
.callback + ": " + msg.what);
       
}

        msg
.target.dispatchMessage(msg);

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

2. Handler

(1)發送消息

Handler支持2種消息類型,即Runnable和Message。因此發送消息提供了post(Runnable r)和sendMessage(Message msg)兩個方法。從下面源碼可以看出Runnable賦值給了Message的callback,最終也是封裝成Message對象對象。學姐個人認爲外部調用不統一使用Message,應該是兼容Java的線程任務,學姐認爲這種思想也可以借鑑到平常開發過程中。發送的消息都會入隊到MessageQueue隊列中。

(2)處理消息

Looper循環過程的時候,是通過dispatchMessage(Message msg)對消息進行處理。處理過程:先看是否是Runnable對象,如果是則調用handleCallback(msg)進行處理,最終調到Runnable.run()方法執行線程;如果不是Runnable對象,再看外部是否傳入了Callback處理機制,若有則使用外部Callback進行處理;若既不是Runnable對象也沒有外部Callback,則調用handleMessage(msg),這個也是我們開發過程中最常覆寫的方法了。

(3)移除消息

removeCallbacksAndMessages(),移除消息其實也是從MessageQueue中將Message對象移除掉。

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

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

public final Message obtainMessage()
{
   
return Message.obtain(this);
}

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

public final boolean sendMessage(Message msg)
{
   
return sendMessageDelayed(msg, 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);
}

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

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg
.target = this;
   
if (mAsynchronous) {
        msg
.setAsynchronous(true);
   
}
   
return queue.enqueueMessage(msg, uptimeMillis);
}

public final void removeCallbacksAndMessages(Object token) {
    mQueue
.removeCallbacksAndMessages(this, token);
}

3. MessageQueue

(1)消息入隊

消息入隊方法enqueueMessage(Message msg, long when)。其處理過程如下:

待入隊的Message標記爲InUse,when賦值

若消息鏈表mMessages爲空爲空,或待入隊Message執行時間小於mMessage鏈表頭,則待入隊Message添加到鏈表頭

若不符合以上條件,則輪詢鏈表,根據when從低到高的順序,插入鏈表合適位置

(2)消息輪詢

next()依次從MessageQueue中取出Message

(3)移除消息

removeMessages()可以移除消息,做的事情實際上就是將消息從鏈表移除,同時將移除的消息添加到消息池,提供循環複用。

boolean enqueueMessage(Message msg, long when) {
   
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("MessageQueue", e.getMessage(), e);
            msg
.recycle();
           
return false;
       
}

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

       
// We can assume mPtr != 0 because mQuitting is false.
       
if (needWake) {
            nativeWake
(mPtr);
       
}
   
}
   
return true;
}

Message next() {
   
// Return here if the message loop has already quit and been disposed.
   
// This can happen if the application tries to restart a looper after quit
   
// which is not supported.
   
final long ptr = mPtr;
   
if (ptr == 0) {
       
return null;
   
}

   
int pendingIdleHandlerCount = -1; // -1 only during first iteration
   
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 && msg.target == null) {
               
// Stalled by a barrier.  Find the next asynchronous message in the queue.
               
do {
                    prevMsg
= msg;
                    msg
= msg.next;
               
} while (msg != null && !msg.isAsynchronous());
           
}
           
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.
                    mBlocked
= false;
                   
if (prevMsg != null) {
                        prevMsg
.next = msg.next;
                   
} else {
                        mMessages
= msg.next;
                   
}
                    msg
.next = null;
                   
if (false) Log.v("MessageQueue", "Returning message: " + msg);
                   
return msg;
               
}
           
} else {
               
// No more messages.
                nextPollTimeoutMillis
= -1;
           
}

           
// Process the quit message now that all pending messages have been handled.
           
if (mQuitting) {
                dispose
();
               
return null;
           
}

           
// If first time idle, then get the number of idlers to run.
           
// Idle handles only run if the queue is empty or if the first message
           
// in the queue (possibly a barrier) is due to be handled in the future.
           
if (pendingIdleHandlerCount < 0
                   
&& (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount
= mIdleHandlers.size();
           
}
           
if (pendingIdleHandlerCount <= 0) {
               
// No idle handlers to run.  Loop and wait some more.
                mBlocked
= true;
               
continue;
           
}

           
if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers
= new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
           
}
            mPendingIdleHandlers
= mIdleHandlers.toArray(mPendingIdleHandlers);
       
}

       
// Run the idle handlers.
       
// We only ever reach this code block during the first iteration.
       
for (int i = 0; i < pendingIdleHandlerCount; i++) {
           
final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers
[i] = null; // release the reference to the handler

           
boolean keep = false;
           
try {
                keep
= idler.queueIdle();
           
} catch (Throwable t) {
               
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
           
}

           
if (!keep) {
               
synchronized (this) {
                    mIdleHandlers
.remove(idler);
               
}
           
}
       
}

       
// Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount
= 0;

       
// While calling an idle handler, a new message could have been delivered
       
// so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis
= 0;
   
}
}

void removeMessages(Handler h, int what, Object object) {
   
if (h == null) {
       
return;
   
}

   
synchronized (this) {
       
Message p = mMessages;

       
// Remove all messages at front.
       
while (p != null && p.target == h && p.what == what
               
&& (object == null || p.obj == object)) {
           
Message n = p.next;
            mMessages
= n;
            p
.recycleUnchecked();
            p
= n;
       
}

       
// Remove all messages after front.
       
while (p != null) {
           
Message n = p.next;
           
if (n != null) {
               
if (n.target == h && n.what == what
                   
&& (object == null || n.obj == object)) {
                   
Message nn = n.next;
                    n
.recycleUnchecked();
                    p
.next = nn;
                   
continue;
               
}
           
}
            p
= n;
       
}
   
}
}

4. Message

(1)消息創建

Message.obtain()創建消息。若消息池鏈表sPool不爲空,則從sPool中獲取第一個,flags標記爲UnInUse,同時從sPool中移除,sPoolSize減1;若消息池鏈表sPool爲空,則new Message()

(2)消息釋放

recycle()將消息釋放,從內部實現recycleUnchecked()可知,將flags標記爲InUse,其他各種狀態清零,同時將Message添加到sPool,且sPoolSize加1

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */

public static Message obtain() {
   
synchronized (sPoolSync) {
       
if (sPool != null) {
           
Message m = sPool;
            sPool
= m.next;
            m
.next = null;
            m
.flags = 0; // clear in-use flag
            sPoolSize
--;
           
return m;
       
}
   
}
   
return new Message();
}

/**
 * Return a Message instance to the global pool.
 * <p>
 * You MUST NOT touch the Message after calling this function because it has
 * effectively been freed.  It is an error to recycle a message that is currently
 * enqueued or that is in the process of being delivered to a Handler.
 * </p>
 */

public void recycle() {
   
if (isInUse()) {
       
if (gCheckRecycle) {
           
throw new IllegalStateException("This message cannot be recycled because it "
                   
+ "is still in use.");
       
}
       
return;
   
}
    recycleUnchecked
();
}

/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */

void recycleUnchecked() {
   
// Mark the message as in use while it remains in the recycled object pool.
   
// Clear out all other details.
    flags
= FLAG_IN_USE;
    what
= 0;
    arg1
= 0;
    arg2
= 0;
    obj
= null;
    replyTo
= null;
    sendingUid
= -1;
   
when = 0;
    target
= null;
    callback
= null;
    data
= null;

   
synchronized (sPoolSync) {
       
if (sPoolSize < MAX_POOL_SIZE) {
           
next = sPool;
            sPool
= this;
            sPoolSize
++;
       
}
   
}
}

5. HandlerThread

由於Java中的Thread是沒有消息循環機制的,run()方法執行完,線程則結束。HandlerThread通過使用Looper實現了消息循環,只要不主動調用HandlerThread或Looper的quit()方法,循環就是一直走下去。

public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;

public HandlerThread(String name) {
   
super(name);
    mPriority
= Process.THREAD_PRIORITY_DEFAULT;
}

@Override
public void run() {
    mTid
= Process.myTid();
   
Looper.prepare();
   
synchronized (this) {
        mLooper
= Looper.myLooper();
        notifyAll
();
   
}
   
Process.setThreadPriority(mPriority);
    onLooperPrepared
();
   
Looper.loop();
    mTid
= -1;
}

public Looper getLooper() {
   
if (!isAlive()) {
       
return null;
   
}
   
   
// If the thread has been started, wait until the looper has been created.
   
synchronized (this) {
       
while (isAlive() && mLooper == null) {
           
try {
                wait
();
           
} catch (InterruptedException e) {
           
}
       
}
   
}
   
return mLooper;
}

public boolean quit() {
   
Looper looper = getLooper();
   
if (looper != null) {
        looper
.quit();
       
return true;
   
}
   
return false;
}
}

總結

  1. 關鍵類:HandlerThread、Handler、Looper、MessageQueue、Messaga
  2. MessageQueue數據結構,鏈表。
發佈了28 篇原創文章 · 獲贊 23 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章