Handler消息取出和處理
消息的取出
消息的取出主要是通過Looper的loop方法。
Looper.loop()主要是消息循環,從消息隊列中獲取消息,分發消息到Handler中。
查看一下loop的源碼。
public static void loop() {
// --- 1.獲取當前Looper的消息隊列MessageQueue -----
// 第一步
// 獲取當前Looper對象
final Looper me = myLooper();
// myLooper()的作用是返回sThreadLocal存儲的Looper實例
// 若me爲null,則拋出異常
// 所以在執行loop()方法之前,必須執行prepare()方法,prepare() //的作用是創建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. 消息循環,無限循環 --------------
// 第三步
for (;;) {
// 從MessageQueue中取出消息
// 第四步
Message msg = queue.next(); // might block
// 如果消息爲空,則退出循環
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ......代碼省略
final long dispatchEnd;
try {
// 第五步
// 分發消息到對應的Handler
// 把消息派發到msg的target屬性
//target屬性實際上是一個handler對象
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ......代碼省略
// 第六步
// 回收消息
msg.recycleUnchecked();
}
}
消息循環Looper.loop(),主要作用的取出消息,通過以上代碼分析,大致分爲六步:
- 第一步,獲取Looper對象
- 第二步,獲取Looper實例中的消息隊列對象MessageQueue
- 第三步,while()無限循環遍歷
- 第四步,通過queue.next()從MessageQueue中取出一個Message對象
- 第五步,通過msg.target.dispatchMessage(msg)來處理消息
- 第六步,通過 msg.recycleUnchecked()來回收消息
其中第一二三步就別少了,第六步在
Message源碼
下面介紹第四步和第五步。
MessageQueue.next()方法
MessageQueue.next()主要是從MessageQueue中取出一個Message對象,源碼如下:
Message next() {
//mPtr是關聯到native層的一個long型變量,只有當MessageQueue被
// 拋棄的時候(調用disposed()方法),mPtr才爲0,這時直接return
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 記錄空閒時處理的IdlerHandler的數量
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) {
// 獲取開機到現在的時間
final long now = SystemClock.uptimeMillis();
//標記前一個message
Message prevMsg = null;
//獲取消息隊列中鏈表表頭的第一個元素
Message msg = mMessages;
//判斷Message是否是同步屏障,如果是則執行循環,攔截所有同步消 //息,直到取到第一個異步消息爲止。
if (msg != null && msg.target == null) {
// 如果進入這個循環,則表示MessageQueue的第一個message就是
//同步屏障消息
// 循環遍歷,攔截所有同步消息,直到取出第一個異步消息
do {
prevMsg = msg;
msg = msg.next;
//退出循環的條件,msg==null,表示循環結束,
//msg.isAsynchronous()爲ture表示是異步消息,則退出循環
} while (msg != null &&!msg.isAsynchronous());
}
if (msg != null) {
//判斷msg是否達到了執行時間
if (now < msg.when) {
//msg沒有達到執行時間,設置一下阻塞時間
//nextPollTimeoutMillis,進入下次循環的時候會調用
//nativePollOnce(ptr,nextPollTimeoutMillis)進行阻塞
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//msg已到執行時間
//此時有消息,不能阻塞
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爲正在使用的狀態
msg.markInUse();
// 返回msg
return msg;
}
} else {
//沒有消息,會一直阻塞,直到被喚醒
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//如果是退出狀態,關閉消息隊列,返回null,拋棄MessageQueue
if (mQuitting) {
dispose();
return null;
}
// 下面的代碼是處理一些不緊急的任務
// 如果沒有符合條件的消息,會處理一些不緊急的任務
// 每次調用MessageQueue.next(),這些代碼只執行一次
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount =mIdleHandlers.size();
}
// 當執行過一次不緊急任務時,pendingIdleHandlerCount爲
//0,此時不會執行下面的代碼,直接開始下一步循環
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.
//開始循環執行所有的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只會在消息隊列阻塞之前執行一次,執行之後改標示設
//置爲0,之後就不會再執行,一直到下一次調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.
//當執行了IdleHandler 的 處理之後,會消耗一段時間,這時候消息
//隊列裏的可能有消息已經到達可執行時間,所以重置該變量回去重新檢
//查消息隊列。
nextPollTimeoutMillis = 0;
}
}
總的來說,MessageQueue的next()方法獲取一個Message的時候,會執行下面的操作:
MessageQueue先判斷消息隊列中是否有同步屏障消息的存在,如果有的話,會循環取出第一個異步消息。
當MessageQueue沒有可以處理的消息的時候,它會進入阻塞狀態,等待新消息的到來,在阻塞之前它會執行一次 IdleHandler處理不緊急的任務,所謂的阻塞其實就是不斷的循環查看是否有新的消息進入隊列中。
當MessageQueue被關閉的時候,其成員變量mQuitting會被標記爲true,
然後next()返回null,在Looper.loop()的源碼中可以看到,當獲取的message爲null時,會直接return。
msg.target.dispatchMessage(msg)
msg.target.dispatchMessage(msg)主要是進行消息的分發。
/**
* 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不爲空,則執行handleCallback(Message),然後最終調用message.callback.run()。
- 2.如果msg.callback爲空,但mCallback不爲空,則執行mCallback.handleMessage(msg)。
- 3.如果msg.callback 爲空,且mCallback也爲空,則執行handleMessage()方法。
在大多數情況下,消息分發後的處理是第三種情況,即handleMessage(msg),這一般是通過複寫該方法的方式實現消息處理的業務邏輯。
以上就是消息的取出和處理的全部內容。