Android應用是事件驅(qū)動的,也可以說是Message來驅(qū)動的。每個進程中都有一個默認的消息隊列MessageQueue,其維護了一個待處理的消息列表,Looper不斷地從中取出消息、處理消息。此時我們不禁會抱有一個疑問,在應用運行期間,系統(tǒng)豈不是會不斷地創(chuàng)建Message、處理Message、銷毀Message?答案當然是否定的,Android作為一個成熟的系統(tǒng)平臺,自然不會輕易地采用大量重復構(gòu)建這種低效耗能的運作方式,本篇文章我們就來了解一下Message。
Message
首先看文檔對Message的描述:
Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.
While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.
其大概意思是,Message類被用于發(fā)送給Handler,其可以存儲一些描述信息和任意數(shù)據(jù)類型的對象。Message有兩個額外的int字段和一個額外object字段,可以滿足很多情況下的需求配置。雖然Message的構(gòu)造方法是public的,但是創(chuàng)建Message最好的方法還是去調(diào)用Message.obtain()方法或者調(diào)用一系列Handler.obtainMessage()的方法,這些方法會從一個回收池中去獲取Message對象。
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {}
從文檔中我們可以大致了解到,Android的消息機制并不會大量重復創(chuàng)建Message對象,而是重復利用在消息池中已存在的Message對象。從Handler類源碼中我們可以看到,Handler中所有需要構(gòu)建Message對象的地方,包括一系列Handler.obtainMessage()的方法,其實質(zhì)上都是通過Message.obtain來獲得的。
在開始分析obtain方法之前我們先來看看Message類中幾個重要的字段:next字段的注釋我們可以發(fā)現(xiàn)Message的消息池實質(zhì)上就是個鏈表,而這個next指向下一個Message對象;sPoolSync是一個普通Object對象,用于在獲取Message對象時進行同步鎖;mPool字段就是一個Message對象,mPool是鏈表中的第一個Message對象;sPoolSize字段表示的是消息池已有Message的數(shù)量;MAX_POOL_SIZE表示消息池的最大容納數(shù)量。
// sometimes we store linked lists of these things
/*package*/ Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
現(xiàn)在我們來看看Message.obtain()方法:如果消息鏈中沒有消息時,就會new一個新的Message對象并返回;如果消息鏈中有可用對象,就會獲取到當前鏈表的mPool,而后將mPool指向下一個Message對象。
/**
* 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) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
當鏈表中有可用對象時,obtain方法是從鏈表中獲取Message對象,但是沒有對象時,obtain方法中創(chuàng)建的Message又是怎么添加到消息池中的呢?說到這里,不知大家還記不記得,上一篇中在Looper.loop方法中,每一條Message被處理后最終都會調(diào)用Message.recycleUnchecked()方法,沒錯,答案就在這個方法里,我們來看看Message.recycleUnchecked()方法的源碼。
/**
* 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) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
從代碼中可以看出,recycleUnchecked()方法有兩個作用:首先,清空消息狀態(tài),同時設置flags = FLAG_IN_USE,表明該消息已被使用,這個flags在obtain()方法中會被設置為0;然后,如果消息池的大小未超過最大容納數(shù)量,就將自身添加到鏈表的表頭。也就是說,Message并不是在創(chuàng)建時而是在回收時被添加到消息池中的。
命令模式
Android消息機制實質(zhì)上是一個非典型的命令模式,所以這里簡單說一下命令模式。
定義:將一個請求封裝成一個對象,從而讓你使用不同的請求把客戶端參數(shù)化,對請求排隊或者記錄請求日志,可以提供命令的撤銷和恢復功能。
命令模式中主要有三種角色:
Receiver:接收者角色,命令的最終執(zhí)行者。
Command:命令角色,需要執(zhí)行的所有命令都是在這里聲明的。
Invoker:調(diào)用者角色,接受命令并執(zhí)行命令。

命令模式的優(yōu)點:
類間解耦,將調(diào)用者角色與接收者角色實現(xiàn)解耦,調(diào)用者實現(xiàn)功能時只需調(diào)用Command抽象類的execute方法就可以,不需要了解到底是哪個接收者執(zhí)行;可擴展性好,Command的子類可以非常容易地擴展,而調(diào)用者Invoker和高層次的模塊Client不產(chǎn)生嚴重的代碼耦合。
命令模式的缺點:
命令模式主要的缺點就是Command類膨脹的問題,如果有N個命令,Command的子類不是幾個而是N個。
使用場景:
需要抽象出待執(zhí)行的動作,然后以參數(shù)的形式提供出來——類似于過程設計中的回調(diào)機制,而命令模式正是回調(diào)機制的一個面向?qū)ο蟮奶娲贰?br>
在不同的時刻指定、排列和執(zhí)行請求。一個命令對象可以有與初始請求無關(guān)的生存期。
需要支持取消操作。
支持修改日志功能,這樣當系統(tǒng)崩潰時,這些修改可以被重做一遍。
需要支持事務操作。
回到Android消息機制,上邊說到Android消息機制是一個非典型的命令模式,其分工如下:
接收者:Handler,執(zhí)行消息處理操作;
調(diào)用者:Looper,調(diào)用消息的的處理方法;
命令角色:Message,消息類。
在簡單了解了命令模式之后,相信大家也就理解了為什么Android消息機制為什么會使用命令模式了。不難發(fā)現(xiàn),Android消息機制的接收者和調(diào)用者之間是存在依賴的,似乎與命令模式相違和,但仔細一想倒也無可厚非,設計模式的存在意義就是為了使代碼的更易于維護和擴展,之前說過接收者和調(diào)用者實現(xiàn)解耦,其最大的好處就是讓未知對象之間的調(diào)用關(guān)系更加靈活,而Android消息機制中的接收者、調(diào)用者和命令角色三者角色固定而明確,自然也不必需要這個擴展性,命令角色的單一還解決了命令模式的類膨脹問題。
這就是本篇文章的全部內(nèi)容了,本人的能力和水平有限,難免會出現(xiàn)錯誤,如有發(fā)現(xiàn)還望大家不吝指正。
參考書籍:《Android源碼設計模式解析與實戰(zhàn)》