官方文檔這樣介紹 Handler
Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
意思是每個handler實例都關聯一個線程以及這個線程的message queue,一旦handler創建,它就和線程綁定了。handler的作用是傳遞信息和runnable至message queue ,並且在信息和runnable從消息隊列返回時處理他們。
根據上面的描述,我繪製了一個比較簡單的handler工作原理圖
假設我們現在有兩個線程,線程B想要通過handler發消息給線程A,具體是怎麼一個過程呢。
很簡單,線程A擁有自己的
Handler :1、負責發送消息和runnable到消息隊列 2、處理從消息隊列返回的消息和runnable
消息隊列:負責存放待處理的消息。
Looper :負責循環遍歷消息隊列,根據先進先出原則取出待處理消息發送給handler
當線程B想要發送消息到線程A處理時,它只需要獲取線程A中的handler實例,調用handler的幾個基礎方法發送消息即可,這些消息會被線程A的handler實例傳遞到線程A的消息隊列等待處理,當線程A的looper循環遍歷到他們時,就會被傳遞到線程A的handler實例進行處理,整個過程就完成了。
那麼我們可以看一看源碼,是不是這樣一個過程呢
從handler的構造函數入手
public Handler() {//無參構造函數調用了帶兩個參數的構造函數
this(null, false);
}
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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上面這個方法非常重要!它做了啥事情呢
1、檢查了創建handler的類是不是內部類,如果是內部類是不是靜態的,不然則做出提示
The following Handler class should be static or leaks might occur
這個提示是不是非常眼熟,嘻嘻。
爲什麼要做這個檢查呢,我之前發表過一篇文章進行討論,有興趣可以參考 點擊打開鏈接
2、 mLooper = Looper.myLooper(); 獲取了一個looper對象,如果該對象爲空,則拋出異常
Can't create handler inside thread that has not called Looper.prepare()
嘻嘻,也是很眼熟吧,當我們在子線程創建handler時,這個異常一言不合就拋出了。那麼我們可以看看這個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");
}
sThreadLocal.set(new Looper(quitAllowed));
}
它做的事情很簡單,檢查當前線程有沒有looper對象,有則拋出異常提示線程已經擁有自己的looper對象。沒有就爲當前線程創建一個looper對象
回到handler的構造函數,在獲取了自己的looper對象以後,它又做了什麼
3、mQueue = mLooper.mQueue; 獲取message queue對象
到這裏我們可以看到,Handler的構造函數做了兩件重要的事情,一是創建looper對象,二是獲取消息隊列
接下來,我們看一看Handler是怎麼傳遞消息的,依然從源碼入手
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
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);
}
再多的這裏就不貼了,因爲無論從handler哪一個發送消息的方法入手,最終調用的都是queue.enqueueMessage(msg, uptimeMillis)。我們主要看一看這個enqueueMessage(msg, uptimeMillis)方法是幹了啥 boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
synchronized (this) {
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
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插入到消息隊列中合適的位置,爲了便於理解,還是稍微講幾句它的核心代碼
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;
}
先解釋一下這裏的mMessage,它是處於消息隊列頭部的消息,if判斷語句說的是,如果這個消息隊列頭爲空,或者是新消息的延遲爲0,或者是新消息的延遲小於隊列頭消息的延遲時,直接把新消息置於隊列頭部。不然的話
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;
}
需要根據新消息的延遲將它插入至隊列中合適的位置,這裏的for循環做的就是遍歷消息隊列,依次對比新消息和每一條消息的延遲,直至找到比新消息延遲大的第一條消息。
到這裏,handler的構造函數和消息傳遞,我們都看到了,結合上圖會不會更清晰一點呢。接下來,我們就看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();
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.recycle();
}
}
我們依舊只看最關鍵的代碼
public static void loop() {
final Looper me = myLooper();
……
final MessageQueue queue = me.mQueue;
……
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
……
msg.target.dispatchMessage(msg);
……
msg.recycle();
}
}
這個方法先拿到消息隊列,然後開啓一個for循環,從消息隊列中依次取出每一條消息,然後調用msg.target.dispatchMessage(msg)進行處理
這個msg.target是啥呢,這裏必須說一下,其實這個target就是handler對象
如果我們發送消息時採用的是handler的一系列方法(
sendMessage(Message msg)
sendEmptyMessage(int what)
sendEmptyMessageDelayed(int what, long delayMillis)
……
)
他們最終都會調用到一個方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在這裏handler將自己賦值給message對象的target
如果我們發送消息時採用的是Message的一系列方法
obtain(Handler h) +sendToTarget()
obtain(Handler h, Runnable callback) +sendToTarget()
每一個obtain方法中,都會將handler賦值給message對象的target,這裏就不貼出代碼了。
所以msg.target.dispatchMessage(msg),其實就是message對象調用它所綁定的handler的方法,處理本身了。我們最後看看這個dispatchMessage(msg)方法
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
這個方法做的事情很簡單,判斷回調有沒有被實現,如果回調被實現則調用相應的方法,不然就調用handleMessage方法。
到這裏handler的工作原理和源碼講解就全部結束了,小女子火候尚淺,若有不對的地方,還請各位俠士指教了。O(∩_∩)O~~