回轉壽司你一定吃過!——Android訊息機制(構造)
訊息機制的故事
-
壽司
陳放在壽司碟
上,壽司碟
按先後順序被排成佇列
送上傳送帶
。傳送帶
被啟動後,壽司
挨個呈現到你面前,你可以選擇吃或者不吃。
將Android概念帶入後,就變成了Android訊息機制的故事:
壽司碟 --->訊息(Message)
佇列 --->
訊息佇列(MessageQueue)
傳送帶 --->
訊息泵 (Looper)
壽司 ---> 你關心的資料
暫未找到 Handler
在此場景中對應的實體。它是一個更抽象的概念,它即可以生產壽司,又把壽司送上傳送帶,還定義了怎麼享用壽司。暫且稱它為 訊息處理器
吧。
如果打算自己開一家回轉壽司店,下面的問題很關鍵:
- 如何生產壽司(如何構造訊息)
- 如何分發壽司(如何分發訊息)
讓我們帶著這兩個問題,去分析一下訊息機制原始碼。
(ps: 下文中的 粗斜體字 表示引導原始碼閱讀的內心戲)
如何構造訊息
壽司碟是重複利用的,享用完壽司後,它被清洗,然後被存放起來,以便再次利用。沒有哪個老闆會在用餐後把壽司碟銷燬,下次要用就買新的,這樣代價太大。
同樣道理, 構造訊息物件代價也很大,它是否也像壽司碟一樣可以複用?如果是,那訊息存放在哪裡?
讓我們以 Handler.obtainMessage()
為切入點一探究竟:
public final Message obtainMessage(){ return Message.obtain(this); }
它呼叫了 Message.obtain()
,原始碼如下:
public final class Message implements Parcelable { //省略了非關鍵程式碼 ... // sometimes we store linked lists of these things //指向訊息鏈上下一個訊息的引用 /*package*/ Message next; //訊息鏈頭部引用,它是靜態的,可以被所有訊息物件共享 private static Message sPool; //訊息鏈長度 private static int sPoolSize = 0; ... /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { //1. 定義指向訊息鏈頭部引用 Message m = sPool; //2. 定義新的訊息鏈頭部 sPool = m.next; //3. 斷鏈 m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; //返回訊息鏈頭部訊息 return m; } } //如果訊息鏈為空則新建訊息 return new Message(); } ... }
-
連結串列
obtain()
如果對資料結構中的
連結串列
還有映像,obtain()
就是在取連結串列頭。圖示如下:1

2

3
- 訊息池是用連結串列結構實現的。那
Message
一定有一個指向後續結點的“指標” ,果不其然,在其成員變數中找到Message next;
。 - 訊息池頭指標
sPool
是一個Message
型別的靜態變數,這表示所有Message
都共享這一個訊息池。 -
obtain()
是從訊息池中拿訊息, 那一定還有一個方法是往池裡填訊息 ,在Message
類中搜索sPool
使用的地方,找到如下這個方法:
/** * Recycles a Message that may be in-use. * Used internally by the MessageQueue and Looper when disposing of queued Messages. */ void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. //清理訊息攜帶的資料 flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { //限制訊息池大小 if (sPoolSize < MAX_POOL_SIZE) { //1. 回收的訊息接入訊息鏈 next = sPool; //2. 回收的訊息成為訊息鏈新頭部 sPool = this; sPoolSize++; } } }
-
正如猜想的那樣
recycleUnchecked()
會將當前訊息插入到訊息鏈頭部。圖示如下1

2
- 讀到這裡,我們知道“訊息從池中來最終又回到池中去”, 那到底訊息是在什麼時候才會被回收到訊息池呢? 好問題!這個問題要等分析完訊息分發才能解答。但現在我們可以大膽的猜測一下: 承載壽司的碟子會在壽司被享用完之後被廚房回收,那訊息是不是再被處理完之後就被回收了?
總結
Android訊息機制中的“構造訊息”部分講完了,總結一下:訊息的生命週期會經歷“建立-回收-再利用”,所有訊息共享一個連結串列結構的訊息池,它用於存放被回收的訊息。
故事還沒有結束,下一篇會繼續講解“分發訊息”。