中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android中的Message類以及Java對象池的實現

發布時間:2020-06-10 04:24:15 來源:網絡 閱讀:3544 作者:zhlwish 欄目:移動開發

在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


用圖形表示如下:

Android中的Message類以及Java對象池的實現

繼續閱讀代碼,又發現了一個讓人困惑的問題,看這個方法:

    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來實現對象池和這種通過鏈表來實現對象池兩種方法,我看不出來各自有何優缺點,這兩種方法很相似,實現的功能也相似,唯一的不同在于,前者似乎更容易擴展。也許你自己在其他項目中需要對象池的時候,可以借鑒一下這兩種方法。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

包头市| 息烽县| 共和县| 广宁县| 双柏县| 南部县| 县级市| 西乌珠穆沁旗| 贵阳市| 绥化市| 大埔区| 万盛区| 临汾市| 江川县| 灌云县| 德令哈市| 富源县| 伊川县| 冕宁县| 博兴县| 贵南县| 乌鲁木齐县| 阿拉善左旗| 翁源县| 达州市| 大冶市| 南召县| 郁南县| 随州市| 辽阳县| 镶黄旗| 旅游| 松桃| 犍为县| 濮阳市| 无锡市| 肥乡县| 通州区| 东莞市| 崇文区| 临沧市|