1. 程式人生 > >Handler 筆記總結

Handler 筆記總結

本次涉及到的原始碼為 Source for Android 27.


首先,需要明確的就是,Handler 的主要作用就是將一個任務切換到某個指定的執行緒中去執行。

而**切換到指定的執行緒**,說白了就是線上程中呼叫某方法,即可切換到指定執行緒,例如一個方法 m1(),線上程 A 中被呼叫執行,那麼就是切換到了 A,線上程 B 中被呼叫執行,那就是切換到了 B。


對於 Handler 的實現,主要涉及到的有 ThreadLocalMessageQueueLooper

其中,有關於 ThreadLocal 可以參閱:ThreadLocal 實現原理總結


1、MessageQueue

MessageQueue 是一個訊息佇列(雖然內部是基於單鏈表的資料結構實現的),用於儲存 Handler 傳送的 Message。主要涉及到的操作就是 enqueueMessage(),往訊息佇列中插入一條訊息next(),從訊息佇列中讀取一條訊息並從訊息佇列中移除該訊息

原始碼部分的註釋參閱自:Android MessageQueue原始碼分析
該文章會介紹 MessageQueue 訊息的插入 (enqueueMessage) 和讀取 (next),native 層的訊息機制,以及 IdleHandler 和 SyncBarrier 的邏輯原理。

1.1、boolean enqueueMessage(Message msg, long when)

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(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages;// mMessages 指向連結串列的第一個 Message 節點 boolean needWake; // 如果佇列為空,或者當前處理的時間點為0(when 的數值,when 表示 Message 將要執行的時間點), // 或者當前 Message 需要處理的時間點先於佇列中的首節點,那麼就將 Message 放入佇列首部 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p;//將 msg 插入到首部 mMessages = msg;//將當前 msg 複製給成員變數 mMessages,也就是將首節點賦值給 mMessages needWake = mBlocked; } else { // 否則遍歷佇列中 Message,找到 when 比當前 Message1 的 when 大的 Message2, // 將 Message1 插入到該 Message2 之前,如果沒找到則將 Message1 插入到佇列最後。 // 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) { //最終還是會退出該 for 迴圈 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. // 判斷是否需要喚醒,一般是當前佇列為空的情況下,next 那邊會進入睡眠,需要enqueue這邊喚醒 next 函式 if (needWake) { nativeWake(mPtr); } } return true; }

enqueueMessage()總的來說就是將 msg 插入到佇列中,涉及到兩個引數,第一個好理解,而第二個引數則與使用 handler.sendMessageAtTime(msg,1000); 等時設定的延遲時間有關,在將 msg 插入訊息連結串列的時候,會賦值給 message.when,會根據設定的 when 的大小來選擇插入的節點,總的就是 when 越小越在前。

1.2、Message next()

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.
    /* 第一步:初始化操作,如果 mPtr 為 null,則直接返回 null,設定 nextPollTimeoutMillis 為 0,進入下一步。 */
    final long ptr = mPtr;// mPrt 是 native 層的 MessageQueue 的指標
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    
    //無限迴圈的
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        
		/* 第二步:呼叫 nativePollOnce。
		nativePollOnce 有兩個引數,第一個為 mPtr 表示 native 層 MessageQueue 的指標,
		第二個引數 nextPollTimeoutMillis 表示超時返回時間(-1 表示一直等待,0 立刻返回)。
		呼叫這個 nativePollOnce 會等待 wake,如果超過 nextPollTimeoutMillis 時間,
		則不管有沒有被喚醒都會返回。*/
        nativePollOnce(ptr, nextPollTimeoutMillis); // jni 函式
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            /* 第三步:獲取佇列的頭 Message(msg),如果頭 Message 的 target 為 null,則查詢一個非同步 Message 來進行下一步處理。
               當佇列中添加了同步 Barrier 的時候 target 會為 null */
            Message prevMsg = null;
            Message msg = mMessages;//先賦值為訊息連結串列的首節點
            //target 正常情況下都不會為 null,在 postBarrier 會出現 target 為 null 的 Message
            if (msg != null && msg.target == null) {// msg.target 即傳送該 msg 的 Handler 的引用,一般情況下不為 null
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            /* 第四步:判斷上一步獲取的 msg 是否為 null,為 null 說明當前佇列中沒有 msg,
               設定等待時間 nextPollTimeoutMillis 為 -1。
               (當 nextPollTimeoutMillis == -1 就會導致下一次輪詢時在第二步因 jni 函式導致 sleep)
               實際上會等待 enqueueMessage 的 nativeWake 來喚醒。
               (可參閱 1.1 enqueueMessage() 最後那一部分的註釋)
               如果非 null,則下一步。 */
            if (msg != null) {
                /* 第五步:判斷 msg 的執行時間 (when) 是否比當前時間 (now) 的大,如果小,則將 msg 從佇列中移除,並且返回 msg,結束。
                   如果大則設定等待時間 nextPollTimeoutMillis為(int) Math.min(msg.when - now, Integer.MAX_VALUE),
                   執行時間與當前時間的差與 MAX_VALUE 的較小值。執行下一步 */
                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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
            /* 第六部:判斷是否 MessageQueue 是否已經取消,如果取消的話則返回 null,否則下一步 */
            // 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);
        }
        /* 第七步:執行 idle Handle,idle 表示當前有空閒時間的時候執行,而執行到這一步的時候,
           表示訊息佇列處理已經是出於空閒時間了(佇列中沒有 Message,或者頭部 Message 的執行時間(when)在當前時間之後)。
           如果沒有 idle,則繼續第二步,如果有則執行 idle Handler 的 queueIdle 方法,
           我們可以自己新增 Idle Handler 到 MessageQueue 裡面(addIdleHandler 方法),執行完後,回到第二步。 */
        // 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(TAG, "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;
    }
}

next() 方法內部有一個 for(,,) 無限迴圈的方法,在正常情況下,如果訊息佇列裡面沒有訊息,則會因為該迴圈阻塞住(這裡會利用底層技術實現 sleep,參閱詳細的註釋部分,而不是簡單粗暴的一直迴圈,猜測是為了避免效能的浪費),而如果能夠正常的取到 message 則會返回該 message,並將其從訊息佇列中移除。


2、Looper

Looper 則是扮演訊息輪詢的角色,內部維護著一個 MessageQueue 例項,它會不停的利用 messageQueue.next() 從訊息佇列中取 Message,如果有取到則會立即進行處理,否則就會因為 messageQueue.next() 內部的實現機制而被阻塞在那裡。

Handler 在使用的時候,需要在對應的執行緒裡面有例項化的 Looper,否則就會報錯。

new Thread("SubThread") {
    @Override
    public void run() {
    	// 在目標執行緒中建立 Looper 例項(因為是在目標執行緒中呼叫的該方法)
    	// 然後會把該例項儲存在 Looper 的靜態成員變數 sThreadLocal 中
        Looper.prepare(); 
        // 在目標執行緒中建立 Hanlder 例項,
        // 該建構函式內部會把當前執行緒的 Looper 藉助 Looper.sThreadLocal 傳遞給該 Handler 例項
        Handler handler = new Handler();
        Looper.loop();
		
		// 需要等 Looper.loop() 執行完後才能執行
		Log.d("TAG", "After Looper.loop()");
    }
}.start();

2.1、Looper.prepare() 方法

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");
    }
    // 例項話一個 Looper 例項並通過 sThreadLocal 儲存
    sThreadLocal.set(new Looper(quitAllowed));
}

2.2、Looper.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();
    for (;;) {
        //正常情況下,queue.next() 如果不能返回 msg,則會因內部的無限迴圈以及具體的實現邏輯堵塞在該處
        Message msg = queue.next(); // might block
        //queue.next() 返回 null 會使得 loop() 方法結束
        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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            //利用 msg 持有的 target(也就是傳送該 msg 的 Handler)對 msg 進行處理
            //正常情況下,因為 loop() 方法是在目標執行緒中被呼叫
            //進一步的這裡就會在目標執行緒中呼叫
            //因此就實現了執行緒的切換
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (slowDispatchThresholdMs > 0) {
            final long time = end - start;
            if (time > slowDispatchThresholdMs) {
                Slog.w(TAG, "Dispatch took " + time + "ms on "
                        + Thread.currentThread().getName() + ", h=" +
                        msg.target + " cb=" + msg.callback + " msg=" + msg.what);
            }
        }
        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();
    }
}

loop() 比較容易理解,其內部也有一個無限迴圈,目的是為了不斷的從訊息佇列中去取 Message 進行處理。需要注意的是,loop() 內部的無限迴圈的阻塞是因為執行到

Message msg = queue.next();

時,無法從 queue.next() 內部退出而發生。

如果通過 messageQueue.next() 取到了 message,則會通過 message 持有的 target(即傳送該 messagehandler 例項的引用) 呼叫該 hanlder 例項的 dispatchMessage(msg) 方法,從而實現了執行緒的切換。

回顧前面說的 切換到指定的執行緒 的本質,這裡因為 loop() 方法是在 SubThread 中呼叫的,因為 hanlder.dispatchMessage(msg) 也就在 SubThread 執行緒中被呼叫。

2.2、Looper 的退出

一個 Looper 例項可以通過 quit() / quitSafely() 退出。如:

// 直接退出
handler.getLooper().quit();
// 設定一個標記,等把訊息佇列設定標誌之前已有的訊息處理後才退出
handler.getLooper().quitSafely();
public void quit() {
    mQueue.quit(false);
}

public void quitSafely() {
    mQueue.quit(true);
}

可以看到,呼叫 Looper 的退出方法,實際上就是呼叫內部的 MessageQueuequit() 來通知訊息佇列退出,當訊息佇列被標記為退出狀態時,其 next() 方法則會返回 null。

Looper 退出後,呼叫 Handlersendpost 方法會返回 false

在子執行緒中,如果手動建立了一個 Looper,則應在不需要用了之後主動呼叫退出的方法來終止訊息迴圈,否則這個子執行緒就會一直處於等待狀態,而如果 Looper 退出了,則線上程內部才能正常執行 Looper.loop() 之後的邏輯。


3、Handler

3.1、傳送 Message

Handler 可以通過一些列的 send 或者 post 方法來發送 Message

對於 post 方法,實際上還是會將傳遞的 runnable 封裝成一個 message 例項,並將 runnable 賦值給 message.callback

然後,send 或者 post 方法都會走到:

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

很簡單,將 message 插入到內部維護的 messageQueue 中。

3.2、處理 Message

在 2.2 中有說,Looper.loop() 方法內部會將從 messageQueue 中取到的 message 交由對應的 handler 進行處理。

msg.target.dispatchMessage(msg);

HanlderdispatchMessage() 也很簡單。

public void dispatchMessage(Message msg) {
	//	通過 handler.post() 傳遞的 callback
    if (msg.callback != null) { 	
        handleCallback(msg);
    } else {
   		// handler 例項內部的 mCallback 成員變數
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                // 如果 mCallback.handleMessage() 返回 true 則 return
                return;
            }
        }
        // 最後才輪到重寫的 handleMessage()
        handleMessage(msg);
    }
}

public interface Callback {
    public boolean handleMessage(Message msg);
}

Callback 的意義在於可以用建立一個 Handler 的例項但不需要派生 Handler 的子類。
下面就是派生子類的實現例項。

class SunHandler extends Handler {
    @Override
    public void handleMessage(Message msg