最近在學習Android消息機制方面的原理,到了Message這塊的時候發現少有文章進行分析,本着“生死看淡不服就幹”的原則,我深入了源碼層面,對Message巧妙的複用機制進行了解析,並寫下這篇文章,供大家分享學習,如果感覺對你有幫助,歡迎點贊,私信,關注我,您的鼓勵是對我最大的幫助。
在日常的Android開發中,我們經常會使用Handler來進行主子線程之間的消息傳遞,其中我們用的最多的就是Message對象。通過查閱官方文檔或者代碼註釋我們可以發現,對於Message,官方建議我們使用Message.obtain來進行Message對象的獲取。
那麼問題來了,爲什麼Google會建議我們使用Message.obtain來進行對象的獲取而不是讓我們自己new一個Message出來呢?或者說,通過Message.obtain來獲取Message對象有沒有什麼好處呢?
其實答案都在源碼裏了,談到這裏,我們就不得不提一種設計模式——享元模式,這是一種和單例設計模式很相似的設計模式,具體的細節在這裏就不講了,感興趣的話歡迎移步菜鳥教程——享元模式。
下面還是讓我們從源碼開始吧,Message複用原理的入口在Message.obtain中:
/**
從Message池中返回一個Message對象,並且允許分配新的實例
*/
public static Message obtain() {
//先是加了一把對象鎖,這個鎖是使用Object來實現的
synchronized (sPoolSync) {
if (sPool != null) {
//如果Message池中非空,就從池子裏取出一個Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
//如果Message池爲空,就新建一個Message對象出來
return new Message();
}
在obtain的代碼中,我們可以看到,先是判斷了一把Message Pool是不是爲空,如果非空的話就從池子裏取出之前用過的Message進行復用,並且異步地對池子裏 Message的數量進行了更新。但是如果Message池爲空的話說明還沒有Message被放進去,那麼就new出來一個Message對象好了。
到這裏,僅僅是對之前使用過的Message進行了取出,或者新建一個Message的動作,還沒有看到Message到底是怎麼被回收的,別急,讓我們移步Message的recycle方法。
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
//最終調用到了這個方法
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
這句話的意思是:一般來說這個方法是被內部的MessageQueue和Looper在分發排序的Message時候來使用的
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
//先把這個Message標記爲使用狀態,防止被二次回收
//然後把這個Message的相關狀態進行了手動復位
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
//Message的複用機制使用了鏈表數據結構來實現
//但神奇的是這個鏈表沒有藉助任何一種數據結構如LinkedList等實現,而是通過指針的形式,將不
//同的Message對象進行了串聯
//在這裏,先是判斷了一把,如果當前Message池子的大小還沒到限制值
//那麼就把當前Message池子的頭指向了當前Message實例的sPool字段,也就是Message池的頭
//部,當然,next字段和Message池子都屬於Message類型
//並且更新了池子的大小
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
從代碼中我們可以看到,Message的複用機制沒有使用任何一種數據結構,如LinkedArrayList,而是通過Message對象內部的spool和next字段,通過指針的方式來進行對象管理,不得不說,是一種非常巧妙的設計方式,一來降低了設計複雜度,而且由於沒有創建額外的數據容器來管理對象,減輕了內存的壓力,實現了輕量化的目的。
具體的實現過程我找到了一幅圖來進行說明:
在Message池初始狀態下,如果一個Message來到了池子裏,他肯定先是被清空了數據,然後將他的sPool字段指向了Message池的頭部,而next字段指向了空,當第二個Message被回收進來之後,m2的next字段指向了m1,原來m1的next字段仍然保持不變,而Message池子的頭部也就是sPool指向了m2。
這樣週而復始地循環,也就完成了Message的鏈表,進而實現了Message的複用目的。