在 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。