Android 消息機(jī)制深入源碼分析 [ 一 ]
Android 消息機(jī)制之 ThreadLocal 深入源碼分析 [ 二 ]
Android 消息機(jī)制之 Looper 深入源碼分析 [ 三 ]
Android 消息機(jī)制之 Message 與消息對象池的深入源碼分析 [ 四 ]
Android 消息機(jī)制之 MessageQueue 深入源碼分析 [ 五 ]
Android 消息機(jī)制之初識Handler [ 六 ]
Android 消息機(jī)制之 Handler 發(fā)送消息的深入源碼分析 [ 七 ]
Android 消息機(jī)制之 MessageQueue.next() 消息取出的深入源碼分析 [ 八 ]
Android 消息機(jī)制之消息的其他處理深入源碼分析 [ 九 ]
Android 消息機(jī)制總結(jié) [ 十 ]
接上一章的 Looper, 本章開始分析消息機(jī)制中的 Message 與消息對象池.
什么是 Message
- 一個(gè)可以發(fā)送給 Handler 的描述和任意數(shù)據(jù)對象的消息對象, 這個(gè)對象包含兩個(gè)額外的 int 字段和一個(gè)額外的對象字段. 這樣就可以使用在很多情況下不用做分配工作.
- 盡管 Message 的構(gòu)造函數(shù)是公開的, 但是獲取 Message 的對象最好的方式是調(diào)用 Message.obtain() 或者 Handler.obtainMessage(), 因?yàn)檫@樣是直接從一個(gè)可回收的消息對象池中獲取 Message 對象.
成員變量 what
- 官方: 它是用戶定義 Message 的標(biāo)識符, 用以分辨消息的內(nèi)容, Handler 擁有自己消息代碼的命名空間, 因此你不用擔(dān)心與其他的 Handler 沖突.
成員變量 arg1 與 arg2
- arg1 與 arg2 都是 Message 的可選變量, 可以用來存放兩個(gè)整數(shù)值, 不用訪問 obj 對象就能讀取的變量. 如果只是幾個(gè)整形的數(shù)值, 相對于使用 setData() 方法, 使用 arg1/arg2 是較低成本的替代方案.
成員變量 obj
- 官方: 將一個(gè)獨(dú)立的對象發(fā)給接收者. 當(dāng)使用 Messenger 去發(fā)送消息, 并且這個(gè)對象包含 Parcelable 類的時(shí)候, 它必須是非空的, 對于其他的數(shù)據(jù)傳輸, 建議使用 setData() 方法.
剩余成員變量
//回復(fù)跨進(jìn)程的 Messenger
public Messenger replyTo;
//Messenger 發(fā)送的 UID
public int sendingUid = -1;
//正在使用的標(biāo)志值,表示當(dāng)前 Messgae 正處于使用狀態(tài),
//當(dāng) Message 處于消息隊(duì)列中,處于消息池中或者 Handler 正在處理 Messgae 的時(shí)候,它就處于使用狀態(tài)
/*package*/ static final int FLAG_IN_USE = 1 << 0;
//異步標(biāo)志值 表示當(dāng)前 Message 是異步的
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
//消息標(biāo)志值 在調(diào)用 copyFrom()方法時(shí),這個(gè)常量就會被設(shè)置,值其實(shí)和 FLAG_IN_USE 一樣.
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
//消息標(biāo)志, 上面 3 個(gè)常量 FLAG 用在這里.
/*package*/ int flags;
//用于存儲發(fā)送消息的時(shí)間點(diǎn), 以毫秒為單位
/*package*/ long when;
//用于存儲比較復(fù)雜的數(shù)據(jù)
/*package*/ Bundle data;
//用于存儲發(fā)送當(dāng)前 Message 的 Hadnler 對象, 說明 Handler 其實(shí)和 Message 是相互持有引用的.
/*package*/ Handler target;
//用于存儲將會執(zhí)行的 Runnable 對象,
//除了 HandlerMessgae(Message msg) 方法,還可以使用 Runnable 執(zhí)行操作.要注意的是這種方法并不會創(chuàng)建新的線程
/*package*/ Runnable callback;
//指向下一個(gè) Messgae,
/*package*/ Message next;
//這個(gè)靜態(tài)是為了給同步塊提供一個(gè)鎖
private static final Object sPoolSync = new Object();
//這個(gè)靜態(tài)的 Message 是整個(gè)線程池鏈表的頭部, 通過它才能逐個(gè)取出對象池的 Message
private static Message sPool;
//記錄對象池中 Message 的數(shù)量(鏈表的長度)
private static int sPoolSize = 0;
//設(shè)置了對象池中 Message 的最大數(shù)量, 也就是鏈表的最大長度
private static final int MAX_POOL_SIZE = 50;
//該系統(tǒng)是否支持回收的標(biāo)志位
private static boolean gCheckRecycle = true;
1. Message.obtain
在 Message.java 中, 通過 Message.obtain()
方式來獲取 Message
對象的, 一共有 8 個(gè)方法.下面將會針對這些逐個(gè)分析一下.
1.1 public static Message obtain( )
代碼位于 Message.java 126 行
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
有一個(gè)變量為 sPool
, 那么 sPool
到底有什么用呢? 這就涉及到了 Message
的設(shè)計(jì)原理了. 這里就引出了 Message
中的消息對象池了.
消息對象池
private static Message sPool;
乍一看, 好像沒什么, 就是一個(gè) Message
對象而已, sPool
對象默認(rèn)是 null
.
這時(shí)候回頭來看上面的無參 obtain
方法, 內(nèi)部直接 new Message()
返回了. 但是官方又不推薦直接 new Message()
, 所以推斷 sPool
在大部分情況下, 是不會為 null
的. 那么就找一下, 看在什么地方會對 sPool
進(jìn)行賦值.
經(jīng)過搜索后發(fā)現(xiàn), 整個(gè) Message
就有兩次地方對 sPool
進(jìn)行賦值.
- 上面的無參
obtain
方法中. -
void recycleUnchecked()
方法中.
首先第一點(diǎn)已經(jīng)排除了. 那么就直接進(jìn)入到第二點(diǎn)方法內(nèi)查看.
Message.recycleUnchecked()
為了更好的理解 我把 recycleUnchecked() obtain()
放在一起., 并省略一些不重要的代碼
void recycleUnchecked() {
...
if (sPoolSize < MAX_POOL_SIZE) {
// 第一步
next = sPool;
// 第二步
sPool = this;
// 第三步
sPoolSize++;
...
}
}
public static Message obtain() {
synchronized (sPoolSync) {
//第一步
if (sPool != null) {
// 第二步
Message m = sPool;
// 第三步
sPool = m.next;
// 第四步
m.next = null;
// 第五步
m.flags = 0;
// 第六步
sPoolSize--;
return m;
}
}
}
recycleUnchecked()
注意: 變量 next
與 sPool
都是 Message
.
- 第一步:
next = sPool
, 首先因?yàn)橄ο蟪匾婚_始就為 null, 所以此時(shí)的next
應(yīng)該也是為null
.- 第二步:
sPool = this
, 將當(dāng)前Message
對象作為消息池中第一個(gè)可被復(fù)用的對象.- 第三步:
sPoolSize++
,sPoolSize
默認(rèn)為 0, 這個(gè)時(shí)候就變成了 1, 將消息對象池內(nèi)的數(shù)量+1, 這個(gè)數(shù)量是全局共享的.
假設(shè)現(xiàn)在又調(diào)用了一遍 recycleUnchecked
這個(gè)方法, 之前第一遍調(diào)用 recycleUnchecked
中那個(gè)第一個(gè)可被復(fù)用的對象為 Message1
, 依舊執(zhí)行上面三步.
- 第一步:
next = sPool
, 因?yàn)橄ο蟪刂杏幸粋€(gè)Message1
, 所以此時(shí)sPool
為Message1
, 同時(shí)賦值給next
.- 第二步:
sPool = this
, 將當(dāng)前Message
對象作為消息池中第一個(gè)可被復(fù)用的對象. 假設(shè)當(dāng)前這個(gè)為Message2
.- 第三步:
sPoolSize++
,sPoolSize
此時(shí)為 1, ++ 后, 變?yōu)?2.
那么現(xiàn)在,
sPool
消息對象池?cái)?shù)量為 2,sPool
對應(yīng)Message2
,sPool
內(nèi)持有的另一個(gè) Message 對象next
對應(yīng)Message1
.
依次類推, 直到sPoolSize = MAX_POOL_SIZE (MAX_POOL_SIZE = 50)
.
接著看 obtain(), 假設(shè)消息對象池中已經(jīng)有兩個(gè)對象了. 就是 Message1, Message2
.
- 第一步: 判斷
sPool
是否為null
, 為null
就直接創(chuàng)建一個(gè)Message
并返回.- 第二步:
Message m = sPool
, 將消息對象池的頭部賦值給m
, 剛才sPool
對應(yīng)的是Message2
.- 第三步:
sPool = m.next
, 將消息對象池的下一個(gè)可復(fù)用的Message
取出并賦值給消息對象池的頭部, 那么此時(shí),sPool
對應(yīng)Message1
,m
對應(yīng)Message2
.Message1.next
是為null
的.- 第四步:
m.next = null
, 因?yàn)橹耙呀?jīng)把m.next
也就是Message1
賦值給sPool
了. 所以就把m
做為單獨(dú)的一個(gè)Message
來對待.- 第五步
m.flags = 0
, 設(shè)置 m 的標(biāo)記位, 標(biāo)記正在被使用.- 第六步.
sPoolSize--
, 因?yàn)橐呀?jīng)取出了一個(gè),賦值給m
了, 這時(shí)候消息對象池的容量減一.
那么現(xiàn)在, m
就是單獨(dú)的一個(gè) Message
對象. sPool
消息對象池?cái)?shù)量為 1, 對應(yīng)的是 Message1
, 并且 sPool.next = null
, 因?yàn)閺南ο蟪刂腥〕隽?Message2
給了 m
.
那么剩下的是什么操作呢, Message2
取出來了, 就要開始分發(fā)了, 還記得昨天分析的 Looper.loop()
方法嗎, 分發(fā)完 Message2
后最后一步就是調(diào)用了 Message2.recycleUnchecked()
方法, 又將 Message2
放入了消息對象池了, 這樣就完成了一次 Message
的復(fù)用.
(一開始消息對象池就是 null
, 當(dāng)我們第一次調(diào)用 Message.obtain()
的時(shí)候, 其實(shí)就是直接 new
了一個(gè) Message
的. 然后在昨天學(xué)習(xí)的 Looper.loop()
方法的最后, 分發(fā)完消息后, 調(diào)用了 msg.recycleUnchecked()
將我們 new
的 Message
放入了消息對象池.)
- 可以將
sPool
看成一個(gè)指針, 通過next
來將對象組成一個(gè)鏈表. 因?yàn)槊看沃恍枰獜某刈又心贸鲆粋€(gè)對象, 所以不需要關(guān)心池子里具體有多少個(gè)對象, 而是拿出當(dāng)前這個(gè)sPool
所指向的這個(gè)對象就可以了.sPool
從思路上理解就是通過左右移動來完成復(fù)用和回收.
- 把
Message m = sPool
, 然后sPool = m.next
因此第一個(gè)m.next
就等于第二個(gè)message
, 從上圖上看相當(dāng)于指針向后移動了一位, 隨后將第一個(gè)message.next()
的值設(shè)置為 null.如下圖
- 現(xiàn)在這個(gè)鏈表看上去就斷了, 如果
in-use
這個(gè)message
使用完畢了, 怎么回到鏈表中? 這就是recycleUnchecked()
回收了.
這時(shí)候再看下recycleUnchecked()
中的代碼,next = sPool
, 將當(dāng)前sPool
所指向的message
對象賦值給in-use
的next
. 然后sPool=this
, 將sPool
指向第一個(gè)message
對象.從而達(dá)到回收 . 如下圖
這樣鏈表就恢復(fù)了,而且不管是復(fù)用還是回收都是保證線程同步的. 所以始終會形成一條鏈?zhǔn)浇Y(jié)構(gòu).
?
1.2 public static Message obtain(Message orig)
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
- 解析
和
obtain
差不多, 但是是將Message
的所有內(nèi)容賦值一份到新的消息中.
代碼中可以看到首先調(diào)用的是無參obtain
, 從消息對象池中獲取一個(gè)Message
對象m
, 然后把origin
中所有的屬性都賦值給m
.并返回m
.
?
1.2 public static Message obtain(Handler h)
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
- 解析
和無參
obtain
一樣, 但是成員變量中target
的值可以用以指定的值(入?yún)?來替換.
剩下幾個(gè) obtain
方法都基本類似, 都是先調(diào)用了obtain
無參函數(shù), 在重置一些值. 這里就不再一一說明.
主要就是對無參 obtain
方法的理解, recycleUnchecked()
方法的理解以及消息對象池 sPool
的理解. 這三個(gè)方面, 還是要捋清楚的. 下一章一起學(xué)習(xí)分析 MessageQueue
.