概述
Handler 是 Android 中用來進行執行緒間通訊的一種方式,通過它可以將其他執行緒的任務切換到 Handler 所在的執行緒中執行,比如任務執行緒更新 UI,另外 IntentService、AsyncTask 等都使用到了 Handler 來進行執行緒的切換。 Handler 的執行主要依賴於 MessageQueue 和 Looper 的協助,簡單的說就是 Handler 持有 MessageQueue 和 Looper物件,MessageQueue 用於存放其他執行緒傳送過來的 Message,Looper 主要用於 MessageQueue 中取出 Message 然後交給 Handler 去執行,這樣便完成了任務在不同執行緒的切換。
MessageQueue
如上文所述,MessageQueue 是用於存放訊息的類,其內部維持這一個連結串列,連結串列的節點就是 Message。它提供了 enqueueMessage()
來進行插入新的訊息,提供next()
從連結串列中取出訊息,值得注意的是next()
會迴圈地從連結串列中取出 Message 交給 Handler,但如果連結串列為空的話會阻塞這個方法,直到有新訊息到來。
enqueueMessage()
和 next()
原始碼:
boolean enqueueMessage(Message msg, long when) {
//target 即為 Handler
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;
boolean needWake; //用於判斷是否喚醒 next() 執行,當連結串列為空時 next() 會阻塞
//按時間順序找到 msg 應插入的位置
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) {
//呼叫 native 方法喚醒 next() 執行
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 用於阻塞 native 層中的 MessageQueue
* ptr是 native 層的 MessageQueue 的指標
* nextnextPollTimeoutMillis 是 MessageQueue 阻塞時間
* -1 表示持續阻塞,0 表示不阻塞,> 0表示阻塞時間(毫秒)
*/
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;
/**
* 發現一個訊息屏障,找出連結串列中的第一個非同步訊息執行
* 訊息屏障:即 target == null,這個屏障之後的所有同步訊息都不會被執行,
* 即使時間已經到了也不會執行。
*/
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; //表示沒阻塞,enqueueMessage() 中會判斷
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.
//沒有訊息時,設為 -1 阻塞
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();
}
//當空閒任務小於0時,進入下一次迴圈,由於
// nextPollTimeoutMillis = -1; 而會阻塞
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(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.
//在處理空閒任務時可能會有新的 Message 加入佇列,故設為 0 試探是否有新的訊息
nextPollTimeoutMillis = 0;
}
}
複製程式碼
Looper
Looper 主要用於從 MessageQueue 中取出訊息,然後呼叫Handler 執行,它提供的 loop()
是個無限迴圈的函式,會不斷地從訊息佇列中取出訊息。在非 UI 執行緒中 Handler 之前通常會先使用Looper.prepare()
,這個函式會去建立一個 Looper 例項,然後把它加入該執行緒 ThreadLocal 中,ThreadLocal 是一個執行緒的內部儲存類,ThreadLocal 中的資料其他執行緒將無法訪問。更多 ThreadLocal。
Looper 的退出方式有兩種,quit()
和quitSafely()
,quit()
呼叫時直接退出,而quitSafely()
要等到處理完已有的所有訊息後才退出。
另外 Looper 還提供了 prepareMainLooper()
來建立 UI 執行緒的 Looper,這個方法會在 ActivityThread 的 main()
中被呼叫,同時也提供getMainLooper()
來獲取 UI 執行緒的 Looper,所以在 UI 執行緒中使用 Handler 之前並不需要呼叫 Looper.prepare()
。
Looper.prepare()
中會先判斷執行緒是否有 Looper 例項,沒有就建立一個 Looper 例項,然後加入本執行緒的 ThreadLocal 中。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//一個執行緒只能有一個 Looper 例項
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
複製程式碼
Looper主要的方法時 loop()
,它是一個無限迴圈的函式,只有當訊息佇列返回 null 是才會結束,而訊息佇列返回空則需要呼叫 Looper 的quit()
或quitSafely()
,這兩個方法會進一步呼叫 MessageQueue 的quit()
方法,即 Looper 退出時才能結束迴圈。
loop()
中呼叫 MessageQueue 的next()
來獲取訊息,所以當next()
阻塞時,loop()
也將阻塞。如過獲得了 Message,將呼叫 msg.target.dispatchMessage()
,這個msg.target
就是Handler,Message 物件中有一個 Handler 成員變數,當 Handler 把訊息加入 MessageQueue 時,呼叫msg.target = this
將自己傳到 Message 當中。
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 (;;) {
//從訊息佇列中獲取訊息,如過next() 阻塞,loop也將阻塞
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 為 Handler 物件,即把 msg 交給 Handler 物件執行
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();
}
}
複製程式碼
Handler
在 Handler 機制中,主要的功能都由 MessageQueue 和 Looper 去實現了,Handler 主要用於往佇列裡插入 Message 和 執行 Looper 取出的 Message 兩個過程。
在 Handler 的構造方法中, Handler 會去獲取本執行緒的 Looper 物件 和 MessageQueue,所以如果執行緒之前為呼叫Looper.prepare()
就會出現異常。
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
//如果不是 Looper 執行緒則出現異常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//獲取MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製程式碼
Handler 提供 post 和 send 兩個系列的方法往佇列裡插入訊息,但最終都將呼叫下面這個方法來進行插入,並且把 Message 的 成員變數 target 設為自身,以便在loop()
取出該 Message 的時候呼叫 Handler 執行。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//把自身賦給 target
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製程式碼
當loop()
取出某個 Message ,並呼叫msg.target.dispatchMessage()
時,將執行以下方法,最終執行定義 Handler 時執行的handleMessage()
。如果為 Message 設定了Runnable,則執行 Runnable.run()
,否則執行 Handle 的 成員變數
Callback 的handleMessage()
(如果有),再不然就是 Handler 的成員方法 handleMessage()
。
public void dispatchMessage(Message msg) {
//這個callbck是個Runnable物件,handleCallback就是執行了Runnable物件.run()
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製程式碼
以上,就是 Handler 整個工作過程。
++:
《Android 開發藝術探索》 深入理解MessageQueue