Android中的Message類以及Java物件池的實現
在Android的android.os.Message類的文件中有這麼一句話:
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類的構造方法是public的,你可以直接通過new來建立一個新的物件,但是最好還是通過Message.obtain()或者Handler.obtainMessage()來建立一個新的Message物件,因為這兩個方法會重用之前建立的但是已經不再使用了的物件例項。
這句話不禁引起了我的興趣,因為之前我寫過一篇部落格《Pool, SimplePool與SynchronizedPool》,在這篇部落格裡面仔細分析了Android是如何實現一個物件池的。裡面提到VelocityTracker就是用SynchronizedPool來實現物件重用的,程式碼如下:
VelocityTracker tracker = VelocityTracker.obtain();
tracker.recycle();
Message類看起來也略有相似,不過經過閱讀Message類的原始碼,發現我錯了,Message類使用了另一種巧妙的方法來實現物件重用。
好了,不賣關子了,Message類使用了一個連結串列來實現物件池,而且是一個前端連結串列,即在前端插入和刪除的連結串列,避免了插入和刪除的時候遍歷整個連結串列。是不是有點出人意料?
首先看一下這段程式碼,去除了Message中其他的攜帶訊息資訊的欄位。已經很明顯可以看出來是一個連結串列了吧。
public final class Message implements Parcelable {
// 省略其他程式碼
Message next; // (1)
private static final Object sPoolSync = new Object(); // (2)
private static Message sPool; // (3)
private static int sPoolSize = 0; // (4)
private static final int MAX_POOL_SIZE = 50;
// 省略其他程式碼
}
(1) 宣告瞭next指標
(2) 物件鎖,Message物件池是執行緒安全的,這樣就需要在向物件池申請和歸還物件時使用鎖
(3) 這裡是關鍵,一個靜態的Message物件,這就是這個連結串列的頭指標了,因為是類變數,因此,整個JVM中只有一個
(4) 當前物件池的大小,後面還限制了這個Message物件池中的物件個數最大為50
用圖形表示如下:
public static Message obtain() {
synchronized (sPoolSync) { // (1)
if (sPool != null) {
Message m = sPool; // (2)
sPool = m.next; // (3)
m.next = null; // (4)
sPoolSize--; // (5)
return m;
}
}
return new Message();
}
這也很容易理解:
(1) 獲取鎖,這裡毫無疑義
(2) 當頭指標指向的物件不為null時,將這個物件賦值給m
(3) 將頭指標指向m的next指標
(4) 將m的next指向null,到這裡位置,我們從以sPool為頭指標的連結串列中取出了第一個元素
(5) 將連結串列size減1
看起來沒錯,疑問是,當第一次獲取物件的時候sPool肯定為null,那麼這個if語句肯定不會執行,會直接執行最後一句return new Message(),直接建立一個物件?這樣不是毫無意義了麼?
看到這裡,似乎感覺發現了一個Android的bug,會有這麼明顯的bug麼?當然不會,靜下心來繼續讀程式碼,讀到這裡的時候豁然開朗了:
public void recycle() {
clearForRecycle(); // (1)
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) { // (2)
next = sPool; // (3)
sPool = this; // (4)
sPoolSize++;
}
}
}
(1) 重置Message中攜帶的訊息
(2) 檢查當前物件池的大小是不是已經超過了最大值,如果當前佇列已經滿了,就不管這個物件了,讓JVM的GC回收好了,這裡保證了效能的同時兼顧了記憶體消耗
(3) 將當前物件next指標指向頭指標sPool指向的物件
(4) 將頭指標sPool指向當前物件,然後將物件池大小加1
到這裡就明白了:這篇部落格開頭的那句話其實背後還有一些潛臺詞,那就是你必須顯式的呼叫一下recycle()將當前的Message物件歸還到物件池,這個物件池才能發揮其效果,不呼叫recycle()方法,物件不會歸還,會被JVM GC回收。
也就是說下面兩句話必須是成對出現的,不用obtain()而呼叫recycle()會導致不停的建立Message物件直到超過MAX_POOL_SIZE的限制而被物件池扔掉;通過obtain()申請物件而不用recycle()歸還會導致物件池被消耗乾淨而不停申請新物件。
Message msg = Message.obtain();
msg.recycle();
所以文件再完整還是不如看程式碼。
對於通過SynchronizedPool來實現物件池和這種通過連結串列來實現物件池兩種方法,我看不出來各自有何優缺點,這兩種方法很相似,實現的功能也相似,唯一的不同在於,前者似乎更容易擴充套件。也許你自己在其他專案中需要物件池的時候,可以借鑑一下這兩種方法。
相關文章
- Java 中的物件池實現Java物件
- java中的類與物件Java物件
- Android執行緒池的原理以及專案中實踐Android執行緒
- Unity實現簡單的物件池Unity物件
- Android開發Message原始碼分析【享元模式|物件池】Android原始碼模式物件
- Java類、物件以及(靜態)方法的探討Java物件
- JAVA 將介面的引用指向實現類的物件Java物件
- Java中的類與物件詳解Java物件
- 用Java實現一個通用併發物件池Java物件
- 物件池簡單實現物件
- Java中各Set實現類的效能分析Java
- 如何在java類中呼叫websphere中的jndi物件JavaWeb物件
- Java 物件實現 Serializable 的原因Java物件
- java學習中對類和物件的理解Java物件
- 關於Java中的類和物件筆記Java物件筆記
- Java執行緒池實現原理及其在美團業務中的實踐Java執行緒
- java實現下載器(以及建立一個URL物件)Java物件
- java中多型的理解——父類引用指向子類物件Java多型物件
- Android中實現類似iOS的SwitchButton控制元件AndroidiOS控制元件
- 自己動手實現Java中的StringBuffer類Java
- Android訊息機制Message訊息池Android
- Java 中建立子類物件會建立父類物件麼?Java物件
- 原生js實現物件的深克隆以及淺克隆JS物件
- Java 執行緒池的原理與實現Java執行緒
- 10 Python物件導向程式設計:類和物件以及和Java的對比Python物件程式設計Java
- Java反射以及在Android中的特殊應用Java反射Android
- java裡的物件和類Java物件
- Java中類與物件的關係與區別Java物件
- Java 的抽象類, 介面以及內部類Java抽象
- Javascript 類與靜態類的實現-js物件導向JavaScriptJS物件
- Scala中的包以及包物件物件
- Java中的字串池概念Java字串
- Java 的異常以及File類Java
- 實現多項式的JAVA類Java
- Android中實現JSON字串和JSON物件的轉換AndroidJSON字串物件
- Java GenericObjectPool 物件池化技術--SpringBoot sftp 連線池工具類JavaObject物件Spring BootFTP
- [轉]Java 執行緒池的原理與實現Java執行緒
- cpp中的類和物件物件