Handler 筆記總結
本次涉及到的原始碼為 Source for Android 27.
首先,需要明確的就是,Handler 的主要作用就是將一個任務切換到某個指定的執行緒中去執行。
而**切換到指定的執行緒
**,說白了就是線上程中呼叫某方法,即可切換到指定執行緒,例如一個方法 m1()
,線上程 A 中被呼叫執行,那麼就是切換到了 A,線上程 B 中被呼叫執行,那就是切換到了 B。
對於 Handler
的實現,主要涉及到的有 ThreadLocal
、MessageQueue
、Looper
。
其中,有關於 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
(即傳送該 message
的 handler
例項的引用) 呼叫該 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
的退出方法,實際上就是呼叫內部的 MessageQueue
的 quit()
來通知訊息佇列退出,當訊息佇列被標記為退出狀態時,其 next()
方法則會返回 null。
Looper
退出後,呼叫 Handler
的 send
或 post
方法會返回 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);
而 Hanlder
的 dispatchMessage()
也很簡單。
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