Handler消息取出和處理
消息的取出
消息的取出主要是通過Looper的loop方法。
Looper.loop()主要是消息循環(huán),從消息隊列中獲取消息,分發(fā)消息到Handler中。
查看一下loop的源碼。
public static void loop() {
// --- 1.獲取當前Looper的消息隊列MessageQueue -----
// 第一步
// 獲取當前Looper對象
final Looper me = myLooper();
// myLooper()的作用是返回sThreadLocal存儲的Looper實例
// 若me為null,則拋出異常
// 所以在執(zhí)行l(wèi)oop()方法之前,必須執(zhí)行prepare()方法,prepare() //的作用是創(chuàng)建Looper實例
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 第二步
// 獲取Looper實例中的消息隊列對象MessageQueue
final MessageQueue queue = me.mQueue;
// ......代碼省略
//------ 2. 消息循環(huán),無限循環(huán) --------------
// 第三步
for (;;) {
// 從MessageQueue中取出消息
// 第四步
Message msg = queue.next(); // might block
// 如果消息為空,則退出循環(huán)
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ......代碼省略
final long dispatchEnd;
try {
// 第五步
// 分發(fā)消息到對應的Handler
// 把消息派發(fā)到msg的target屬性
//target屬性實際上是一個handler對象
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ......代碼省略
// 第六步
// 回收消息
msg.recycleUnchecked();
}
}
消息循環(huán)Looper.loop(),主要作用的取出消息,通過以上代碼分析,大致分為六步:
- 第一步,獲取Looper對象
- 第二步,獲取Looper實例中的消息隊列對象MessageQueue
- 第三步,while()無限循環(huán)遍歷
- 第四步,通過queue.next()從MessageQueue中取出一個Message對象
- 第五步,通過msg.target.dispatchMessage(msg)來處理消息
- 第六步,通過 msg.recycleUnchecked()來回收消息
其中第一二三步就別少了,第六步在
Message源碼
下面介紹第四步和第五步。
MessageQueue.next()方法
MessageQueue.next()主要是從MessageQueue中取出一個Message對象,源碼如下:
Message next() {
//mPtr是關聯(lián)到native層的一個long型變量,只有當MessageQueue被
// 拋棄的時候(調用disposed()方法),mPtr才為0,這時直接return
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 記錄空閑時處理的IdlerHandler的數(shù)量
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// native層用到的變量,在nativePollOnce(ptr,
//nextPollTimeoutMillis)使用,
//如果nextPollTimeoutMillis=-1,一直阻塞不會超時,
//如果nextPollTimeoutMillis=0,不會阻塞,立即返回,
//如果nextPollTimeoutMillis>0,最長阻塞nextPollTimeoutMillis
//毫秒(超時),如果期間有程序喚醒會立即返回。
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞方法
nativePollOnce(ptr, nextPollTimeoutMillis);
// 同步鎖
synchronized (this) {
// 獲取開機到現(xiàn)在的時間
final long now = SystemClock.uptimeMillis();
//標記前一個message
Message prevMsg = null;
//獲取消息隊列中鏈表表頭的第一個元素
Message msg = mMessages;
//判斷Message是否是同步屏障,如果是則執(zhí)行循環(huán),攔截所有同步消 //息,直到取到第一個異步消息為止。
if (msg != null && msg.target == null) {
// 如果進入這個循環(huán),則表示MessageQueue的第一個message就是
//同步屏障消息
// 循環(huán)遍歷,攔截所有同步消息,直到取出第一個異步消息
do {
prevMsg = msg;
msg = msg.next;
//退出循環(huán)的條件,msg==null,表示循環(huán)結束,
//msg.isAsynchronous()為ture表示是異步消息,則退出循環(huán)
} while (msg != null &&!msg.isAsynchronous());
}
if (msg != null) {
//判斷msg是否達到了執(zhí)行時間
if (now < msg.when) {
//msg沒有達到執(zhí)行時間,設置一下阻塞時間
//nextPollTimeoutMillis,進入下次循環(huán)的時候會調用
//nativePollOnce(ptr,nextPollTimeoutMillis)進行阻塞
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//msg已到執(zhí)行時間
//此時有消息,不能阻塞
mBlocked = false;
//如果還有上一個元素
if (prevMsg != null) {
//上一個元素的next越過自己指向下一個元素
prevMsg.next = msg.next;
} else {
//如果沒有上一個元素,說明是消息隊列的頭元素,直接
//讓第二個元素變成頭元素
mMessages = msg.next;
}
//msg要取出,msg的next指向null
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//設置msg為正在使用的狀態(tài)
msg.markInUse();
// 返回msg
return msg;
}
} else {
//沒有消息,會一直阻塞,直到被喚醒
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//如果是退出狀態(tài),關閉消息隊列,返回null,拋棄MessageQueue
if (mQuitting) {
dispose();
return null;
}
// 下面的代碼是處理一些不緊急的任務
// 如果沒有符合條件的消息,會處理一些不緊急的任務
// 每次調用MessageQueue.next(),這些代碼只執(zhí)行一次
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount =mIdleHandlers.size();
}
// 當執(zhí)行過一次不緊急任務時,pendingIdleHandlerCount為
//0,此時不會執(zhí)行下面的代碼,直接開始下一步循環(huán)
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.
//開始循環(huán)執(zhí)行所有的IdleHandler,并且根據返回值判斷是否保留
//IdleHandler
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.
// IdleHandler只會在消息隊列阻塞之前執(zhí)行一次,執(zhí)行之后改標示設
//置為0,之后就不會再執(zhí)行,一直到下一次調MessageQueue.next()
//方法。
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.
//當執(zhí)行了IdleHandler 的 處理之后,會消耗一段時間,這時候消息
//隊列里的可能有消息已經到達可執(zhí)行時間,所以重置該變量回去重新檢
//查消息隊列。
nextPollTimeoutMillis = 0;
}
}
總的來說,MessageQueue的next()方法獲取一個Message的時候,會執(zhí)行下面的操作:
MessageQueue先判斷消息隊列中是否有同步屏障消息的存在,如果有的話,會循環(huán)取出第一個異步消息。
當MessageQueue沒有可以處理的消息的時候,它會進入阻塞狀態(tài),等待新消息的到來,在阻塞之前它會執(zhí)行一次 IdleHandler處理不緊急的任務,所謂的阻塞其實就是不斷的循環(huán)查看是否有新的消息進入隊列中。
當MessageQueue被關閉的時候,其成員變量mQuitting會被標記為true,
然后next()返回null,在Looper.loop()的源碼中可以看到,當獲取的message為null時,會直接return。
msg.target.dispatchMessage(msg)
msg.target.dispatchMessage(msg)主要是進行消息的分發(fā)。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
// 1
if (msg.callback != null) {
handleCallback(msg);
} else {
// 2
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
- 1.如果msg.callback不為空,則執(zhí)行handleCallback(Message),然后最終調用message.callback.run()。
- 2.如果msg.callback為空,但mCallback不為空,則執(zhí)行mCallback.handleMessage(msg)。
- 3.如果msg.callback 為空,且mCallback也為空,則執(zhí)行handleMessage()方法。
在大多數(shù)情況下,消息分發(fā)后的處理是第三種情況,即handleMessage(msg),這一般是通過復寫該方法的方式實現(xiàn)消息處理的業(yè)務邏輯。
以上就是消息的取出和處理的全部內容。