收錄:
Android Handler機制 - MessageQueue如何處理消息
Handler 如何做到阻塞
Android篇:2019初中級Android開發社招面試解答(中)
Handler消息機制組成:
- Message(消息):需要被傳遞的消息,消息分為硬件產生的消息(如按鈕、觸摸)和軟件生成的消息。
- MessageQueue(消息隊列):負責消息的存儲與管理,負責管理由 Handler發送過來的Message。讀取后會自動刪除消息,單鏈表維護,插入和刪除上有優勢。在其next()方法中會無限循環,不斷判斷是否有消息,有就返回這條消息并移除。
- Handler(消息處理器):負責Message的發送及處理。主要向消息池發送各種消息事件(Handler.sendMessage())和處理相應消息事件(Handler.handleMessage()),按照先進先出執行,內部使用的是單鏈表的結構。
- Looper(消息池/循環機制):負責關聯線程以及消息的分發,在該線程下從 MessageQueue獲取 Message,分發給Handler,Looper創建的時候會創建一個 MessageQueue,調用loop()方法的時候消息循環開始,其中會不斷調用messageQueue的next()方法,當有消息就處理,否則阻塞在messageQueue的next()方法中。當Looper的quit()被調用的時候會調用messageQueue的quit(),此時next()會返回null,然后loop()方法也就跟著退出。
如何保證looper的唯一性
每個線程只有一個looper,而每個Thread中都又一個關鍵Threadlocal。是用于存放每個線程的looper對象的,存取的方式是通過get/set。相當于一個map的存放方式。鍵位key是當前線程的實例。value就是looper對象。所以每次創建looper都會去ThreadLocal里面找有沒有當前線程的looper。
如何知道 message 發送到哪個handler處理
當使用 Handler.sendMessage() 發送消息時,調用 enqueueMessage 方法內有 msg.target = this 將 Handler 實例賦值給 msg 對象。當 loop() 取出消息時,調用 dispatchMessage 方法根據 target 屬性,回調對應 handler 實例的 handlerMessage 方法處理消息。
具體流程圖:
Handler工作流程
- App啟動時創建一個主線程(UI),接著UI線程會創建一個Looper,同時也會在在Looper內部創建一個消息隊列。而在創鍵Handler的時候取出當前線程的Looper,并通過該Looper對象獲得消息隊列(MessageQueue),然后Handler在子線程中通過MessageQueue.enqueueMessage在消息隊列中添加一條Message。
- 通過Looper.loop() 開啟消息循環不斷輪詢調用 MessageQueue.next(),取得對應的Message并且通過Handler.dispatchMessage傳遞給Handler,最終調用Handler.handlerMessage處理消息。
源碼分析:
- 插入消息
MessageQueue.enqueueMessage:Handler調用sendMessage()發送消息,而Handler內部通過Looper對象得到MessageQueue對象后又調用MessageQueue.enqueueMessage方法。
boolean enqueueMessage(Message msg, long when) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//插入前先消息隊列是否有消息,新的頭,如果被阻止,則喚醒事件隊列。
if (p == null || when == 0 || when < p.when) {
//將消息放進隊列頭部
msg.next = p;
mMessages = msg;
needWake = mBlocked;//指示next()是否被阻止在pollOnce()中以非零超時等待。如果阻塞,則需要喚醒looper
} 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.
/*插入隊列中間。 通常,除非隊列的開頭有障礙并且消息是隊列中最早的
異步消息,否則我們不必喚醒事件隊列。(隊列中消息不為空,并且next()也沒有阻塞)*/
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;
}
// 如果looper阻塞/休眠中,則喚醒looper循環機制處理消息
if (needWake) {
nativeWake(mPtr);//喚醒
}
}
return true;
}
調用nativeWake喚醒(這部分內容出自頭部連接,詳細源碼分析可看前輩的)
//android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr){
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake(){
mLooper->wake();
}
void Looper::wake(){
...
//往mWakeEventFd 中write 1,用以喚醒 looper
ssize_t mWrite = TEMP_FAILURE_READY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
}
既然有寫入消息,那必定要把消息處理掉,所以喚醒了epoll_wait(),然后繼續方法調動awoken(),這個方法就是將之前寫入的1讀出,表示消費這個事件
void Looper::awaken(){
...
//讀取頭部消息,靜默處理掉
TEMP_FAILURE_READY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
隨后在Java 層的next()@MessageQueue 就被喚醒,讀取在enqueueMessage()@MessageQueue 插在隊頭的消息進行處理
- Looper循環讀取消息
當looper循環機制在MessageQueue的next()讀取消息時發現消息隊列中沒有消息時,就會調用nativePollOnce(ptr, nextPollTimeoutMillis);將next()阻塞在PollOnce中。looper也就進入了休眠期。
@UnsupportedAppUsage
Message next() {
// 如果消息循環已經退出并被處理,請返回此處。
// 如果應用程序嘗試退出后不支持的循環程序,則會發生這種情況。
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();
}
//就是在這里根據nextPollTimeoutMillis判斷是否要阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 嘗試檢索下一條消息。 如果找到則返回。
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 被障礙擋住了。 在隊列中查找下一條異步消息。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {//隊列中拿到的消息不為null
if (now < msg.when) {
// 下一條消息尚未準備好。 設置超時以使其在準備就緒時醒來。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 正常返回處理
...
} else {
// 隊列中沒有消息,標記阻塞looper循環進入休眠
nextPollTimeoutMillis = -1;
}
// 現在已處理所有掛起的消息,處理退出消息。
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// 空閑句柄僅在隊列為空或將來要處理隊列中的第一條消息(可能是屏障)時才運行。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
...
}
...
// 將空閑處理程序計數重置為0,這樣我們就不會再次運行它們。
pendingIdleHandlerCount = 0;
// 在調用空閑處理程序時,可能已經傳遞了一條新消息,
//因此返回并再次查找未處理消息,而無需等待。
nextPollTimeoutMillis = 0;
}
}