天天看點

筆記:安卓App消息處理機制

類似Binder機制,MessageQueue、Looper也有底層的C++實作,涉及檔案管道和驅動等。

以下僅從Java層的Looper、Handler和MessageQueue等相關類型的源碼來分析線程消息處理的機制。

Looper用來建立和啟動消息隊列。

Looper對象和線程綁定是通過ThreadLocal實作的。

它提供了各種getter友善擷取線程關聯的MessageQueue和Looper對象。

線程通過執行Looper.prepare()來建立關聯的MessageQueue。

主線程則調用prepareMainLooper()來建立主線程關聯的Looper,友善其它線程向主線程發消息。

它建立了Looper對象,以ThreadLocal存儲它,是以和目前線程綁定。

構造函數中建立了消息隊列:

nativeInit()調用C++代碼建立底層的C++MessageQueue對象。

有了MessageQueue對象以後,接着需要開啟消息循環,使用關聯的Handler來發送、處理消息了。

MessageQueue有點像一個阻塞隊列,它提供MessageQueue.next()用來從隊列中取出一個Message對象。

若沒有消息則調用會阻塞。

Looper.loop()用來開啟對MessageQueue的取消息操作的無限循環。

可以看到,loop()的操作就是無限從queue中調用next()擷取一個新Message對象,然後執行dispatchMessage來處理它。

nativePollOnce()用來檢查隊列中是否有新的消息。

參數nextPollTimeoutMillis表示在暫無消息時此次檢查過程需要休眠的時間:

等于-1:表示無限等待,直到被其它線程喚醒。

等于 0:繼續循環檢查隊列。

大于 0:以nextPollTimeoutMillis的毫米數等待。

使用for (;😉 無限循環檢查是否有消息,直到傳回一個Message對象。

若目前MessageQueue正在退出,則傳回null作為辨別。

每次調用next()時,第一次檢查如果發現沒有要出來的消息,就一次性調用所有在注冊了的IdleHandler回調對象。

MessageQueue和Looper對象都是和某個線程關聯的。

向一個線程發送消息,通過和它綁定的一個Handler對象進行。

執行建立Handler對象的的線程,Handler對象和此線程綁定。

Handler對象的構造函數中,将目前線程關聯的Looper和MessageQueue儲存到其字段中。

每個發送到線程關聯的MessageQueue中的Message對象,主要字段有:

long when字段表示消息預期被處理的時間;

int what表示消息的動作;

Object obj可以攜帶額外的資料。

發送消息的一般形式為:

若不指定when那麼它為目前時間,之後被Looper取出後立即執行;

sendMessage()中将<code>Handler Message.target</code>設定為自身,最執行Handler綁定的隊列的MessageQueue.enqueue()方法。

字段<code>Message Message.next</code>用來指向Message連結清單中的下一個對象。

MessageQueue.mMessages字段指向了它擁有的消息連結清單的頭結點。

mMessages中的消息根據其when的時間排列,時間近的在前面。

enqueueMessage()在添加消息時将它放到mMessages的合适的位置。

mBlocked記錄目前隊列是否處于休眠?

在next()時若最近要處理的消息的when還未到或隊列空時,則在檢查隊列是否空時會主動休眠一段時間。

若新的Message被添加到連結清單頭,且它的when時間到了,那麼就喚醒Looper繼續執行next(),擷取此Message然後處理它。

在Looper.loop()中,每當擷取新的消息後:

target就是發送Message的Hander。

Hander發送消息到其綁定的MessageQueue中。

可見,相關的Looper、MessageQueue、Handler都是和同一個線程關聯的。

從next()的執行在Looper.loop()的循環中進行可知:

Hander.dispatchMessage()的調用就是在Looper關聯的線程中進行。

上面的Message.callback是一個Runnable。

Handler.mCallback是建立它時指定的一個回調對象。

handleMessage()就是子類重寫處理自定義消息的地方。

Looper.loop()執行後,線程“阻塞”,不斷從關聯的MessageQueue中取出消息并處理。

其它線程或在處理某個消息的邏輯中,可以調用Looper.quit()退出目前線程的消息循環。

它執行了MessageQueue.quit()。

這裡設定mQuitting為true。

之後下次調用next()時,傳回null告知Looper.loop()退出循環。

線程的消息循環結束。

(本文使用Atom編寫)