TypedArray 為什么需要調用recycle()

在 Android 自定義 View 的時候,需要使用 TypedArray 來獲取 XML layout 中的屬性值,使用完之后,需要調用 recyle() 方法將 TypedArray 回收。

那么問題來了,這個TypedArray是個什么東西?為什么需要回收呢?TypedArray并沒有占用IO,線程,它僅僅是一個變量而已,為什么需要 recycle?
為了解開這個謎,首先去找官網的 Documentation,到找 TypedArray 方法,得到下面一個簡短的回答:

這里寫圖片描述

告訴我們在確定使用完之后調用 recycle() 方法。于是進一步查看該方法的解釋,如下:
這里寫圖片描述

簡單翻譯下來,就是說:回收 TypedArray,用于后續調用時可復用之。當調用該方法后,不能再操作該變量。

同樣是一個簡潔的答復,但沒有解開我們心中的疑惑,這個TypedArray背后,到底隱藏著怎樣的秘密……

求之不得,輾轉反側,于是我們決定深入源碼,一探其究竟……

首先,是 TypedArray 的常規使用方法:

TypedArray array = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.PieChart,0,0);
try {
    mShowText = array.getBoolean(R.styleable.PieChart_showText,false);
    mTextPos = array.getInteger(R.styleable.PieChart_labelPosition,0);
}finally {
    array.recycle();
}

可見,TypedArray不是我們new出來的,而是調用了 obtainStyledAttributes 方法得到的對象,該方法實現如下:

public TypedArray obtainStyledAttributes(AttributeSet set,
                int[] attrs, int defStyleAttr, int defStyleRes) {
    final int len = attrs.length;
    final TypedArray array = TypedArray.obtain(Resources.this, len);
    // other code .....
    return array;
}

我們只關注當前待解決的問題,其他的代碼忽略不看。從上面的代碼片段得知,TypedArray也不是它實例化的,而是調用了TypedArray的一個靜態方法,得到一個實例,再做一些處理,最后返回這個實例??吹竭@里,我們似乎知道了什么,,,帶著猜測,我們進一步查看該靜態方法的內部實現:

/**
 * Container for an array of values that were retrieved with
 * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
 * or {@link Resources#obtainAttributes}.  Be
 * sure to call {@link #recycle} when done with them.
 *
 * The indices used to retrieve values from this structure correspond to
 * the positions of the attributes given to obtainStyledAttributes.
 */
public class TypedArray {

    static TypedArray obtain(Resources res, int len) {
        final TypedArray attrs = res.mTypedArrayPool.acquire();
        if (attrs != null) {
            attrs.mLength = len;
            attrs.mRecycled = false;

            final int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
            if (attrs.mData.length >= fullLen) {
                return attrs;
            }

            attrs.mData = new int[fullLen];
            attrs.mIndices = new int[1 + len];
            return attrs;
        }

        return new TypedArray(res,
                new int[len*AssetManager.STYLE_NUM_ENTRIES],
                new int[1+len], len);
    }
    // Other members ......
}

仔細看一下這個方法的實現,我想大部分人都明了了,該類沒有公共的構造函數,只提供靜態方法獲取實例,顯然是一個典型的單例模式。在代碼片段的第 13 行,很清晰的表達了這個 array 是從一個 array pool的池中獲取的。

因此,我們得出結論:

程序在運行時維護了一個 TypedArray的池,程序調用時,會向該池中請求一個實例,用完之后,調用 recycle() 方法來釋放該實例,從而使其可被其他模塊復用。

那為什么要使用這種模式呢?答案也很簡單,TypedArray的使用場景之一,就是上述的自定義View,會隨著 Activity的每一次Create而Create,因此,需要系統頻繁的創建array,對內存和性能是一個不小的開銷,如果不使用池模式,每次都讓GC來回收,很可能就會造成OutOfMemory。

這就是使用池+單例模式的原因,這也就是為什么官方文檔一再的強調:使用完之后一定 recycle,recycle,recycle。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 解析:TypedArray 為什么需要調用recycle() - 海盜專欄 - 博客頻道 - CSD...
    zsliger閱讀 477評論 0 0
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,948評論 18 139
  • 1.在C/C++中實現本地方法 生成C/C++頭文件之后,你就需要寫頭文件對應的本地方法。注意:所有的本地方法的第...
    JayQiu閱讀 2,406評論 0 3
  • Jni數據類型 Jni方法 來自 http://blog.chinaunix.net/uid-22028680-i...
    FlyDragonInSky閱讀 935評論 0 0
  • 出武漢向西北行200余公里,入隨州境內,有山曰大洪山。 傳聞這是西漢末年綠林起義的地方,山中有名喚兩王洞的天然巖洞...
    講話學習閱讀 411評論 0 0