這是“Android消息機(jī)制”系列的第一篇文章,系列文章目錄如下:
- 回轉(zhuǎn)壽司你一定吃過(guò)荆陆!——Android消息機(jī)制(構(gòu)造)
- 回轉(zhuǎn)壽司你一定吃過(guò)滩届!——Android消息機(jī)制(分發(fā))
- 回轉(zhuǎn)壽司你一定吃過(guò)!——Android消息機(jī)制(處理)
消息機(jī)制的故事
壽司
陳放在壽司碟
上被啼,壽司碟
按先后順序被排成隊(duì)列
送上傳送帶
帜消。傳送帶
被啟動(dòng)后,壽司
挨個(gè)呈現(xiàn)到你面前浓体,你有三種享用壽司的方法泡挺。
將Android概念帶入后,就變成了Android消息機(jī)制的故事:
壽司碟 ---> 消息(Message)
隊(duì)列 ---> 消息隊(duì)列(MessageQueue)
傳送帶 ---> 消息泵 (Looper)
壽司 ---> 你關(guān)心的數(shù)據(jù)
享用壽司方法 ---> 處理數(shù)據(jù)方式
暫未找到 Handler
在此場(chǎng)景中對(duì)應(yīng)的實(shí)體命浴。它是一個(gè)更抽象的概念娄猫,它即可以生產(chǎn)壽司,又把壽司送上傳送帶生闲,還定義了怎么享用壽司媳溺。暫且稱它為消息處理器
吧。
如果打算自己開(kāi)一家回轉(zhuǎn)壽司店碍讯,下面的問(wèn)題很關(guān)鍵:
- 如何生產(chǎn)壽司(如何構(gòu)造消息)
- 如何分發(fā)壽司(如何分發(fā)消息)
讓我們帶著這兩個(gè)問(wèn)題悬蔽,去分析一下消息機(jī)制源碼。
(ps: 下文中的 粗斜體字 表示引導(dǎo)源碼閱讀的內(nèi)心戲)
如何構(gòu)造消息
壽司碟是重復(fù)利用的捉兴,享用完壽司后蝎困,它被清洗,然后被存放起來(lái)倍啥,以便再次利用禾乘。沒(méi)有哪個(gè)老板會(huì)在用餐后把壽司碟銷毀,下次要用就買新的逗栽,這樣代價(jià)太大盖袭。
同樣道理,構(gòu)造消息對(duì)象代價(jià)也很大,它是否也像壽司碟一樣可以復(fù)用鳄虱?如果是弟塞,那消息存放在哪里?
讓我們以Handler.obtainMessage()
為切入點(diǎn)一探究竟:
public class Handler {
...
public final Message obtainMessage(){
return Message.obtain(this);
}
...
}
public final class Message implements Parcelable {
...
/**
* Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
...
}
- 其中
Message.target
是一個(gè)Handler
類型的成員變量拙已。為啥Message要記錄構(gòu)造它的Handler對(duì)象决记? 好問(wèn)題!但這個(gè)問(wèn)題的解答需要等到分析消息處理的時(shí)候才能解答倍踪,先留個(gè)懸念系宫。 - 構(gòu)造消息調(diào)用鏈的終點(diǎn)是
Message.obtain()
,源碼如下:
public final class Message implements Parcelable {
//省略了非關(guān)鍵代碼
...
// sometimes we store linked lists of these things
//指向消息鏈上下一個(gè)消息的引用
/*package*/ Message next;
//消息鏈頭部引用建车,它是靜態(tài)的扩借,可以被所有消息對(duì)象共享
private static Message sPool;
//消息鏈長(zhǎng)度
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();
}
...
}
- 如果對(duì)數(shù)據(jù)結(jié)構(gòu)中的
鏈表
還有映像,obtain()
就是在取鏈表頭缤至。圖示如下:
1
-
消息池是用鏈表結(jié)構(gòu)實(shí)現(xiàn)的潮罪。那
Message
一定有一個(gè)指向后續(xù)結(jié)點(diǎn)的“指針”,果不其然领斥,在其成員變量中找到Message next;
嫉到。 - 消息池頭指針
sPool
是一個(gè)Message
類型的靜態(tài)變量,這表示所有Message
都共享這一個(gè)消息池月洛。 -
obtain()
是從消息池中拿消息何恶,那一定還有一個(gè)方法是往池里填消息,在Message
類中搜索sPool
使用的地方嚼黔,找到如下這個(gè)方法:
/**
* 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.
//清理消息攜帶的數(shù)據(jù)
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()
會(huì)將當(dāng)前消息插入到消息鏈頭部细层。圖示如下
1
- 讀到這里,我們知道“消息從池中來(lái)最終又回到池中去”隔崎,那到底消息是在什么時(shí)候才會(huì)被回收到消息池呢今艺? 好問(wèn)題韵丑!這個(gè)問(wèn)題要等分析完消息分發(fā)才能解答爵卒。但現(xiàn)在我們可以大膽的猜測(cè)一下:承載壽司的碟子會(huì)在壽司被享用完之后被廚房回收,那消息是不是再被處理完之后就被回收了撵彻?
總結(jié)
Android消息機(jī)制中的“構(gòu)造消息”部分講完了钓株,總結(jié)一下:消息的生命周期會(huì)經(jīng)歷“創(chuàng)建-回收-再利用”,所有消息共享一個(gè)鏈表結(jié)構(gòu)的消息池陌僵,它用于存放被回收的消息轴合。
故事還沒(méi)有結(jié)束,下一篇會(huì)繼續(xù)講解分發(fā)消息