我们没有在使用TypedArray后调用recycle,编译器会提示“This TypedArray should be recycled after use with #recycle()”。
官方的解释是:回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。
在TypedArray后调用recycle主要是为了缓存。当recycle被调用后,这就说明这个对象从现在可以被重用了。TypedArray
内部持有部分数组,它们缓存在Resources类中的静态字段中,这样就不用每次使用前都需要分配内存。你可以看看
TypedArray.recycle()
中的代码:
1 /**
2 * Give back a previously retrieved StyledAttributes, for later re-use.
3 */
4 public void recycle() {
5 synchronized (mResources.mTmpValue) {
6 TypedArray cached = mResources.mCachedStyledAttributes;
7 if (cached == null || cached.mData.length < mData.length) {
8 mXml = null;
9 mResources.mCachedStyledAttributes = this;
10 }
11 }
12 }
参考链接
http://*.com/questions/13805502/why-do-you-have-to-recycle-a-typedarray
http://developer.android.com/reference/android/content/res/TypedArray.html#recycle%28%29
转自:http://blog.csdn.net/Monicabg/article/details/45014327
在 Android 自定义 View 的时候,需要使用 TypedArray 来获取 XML layout 中的属性值,使用完之后,需要调用 recyle() 方法将 TypedArray 回收。
那么问题来了,这个TypedArray是个什么东西?为什么需要回收呢?TypedArray并没有占用IO,线程,它仅仅是一个变量而已,为什么需要 recycle?
为了解开这个谜,首先去找官网的 Documentation,到找 TypedArray 方法,得到下面一个简短的回答:
简单翻译下来,就是说:回收 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();
- }
- 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;
- }
- /**
- * 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。
Android系统事件的recycle原理
最近封装一些功能性的jar包,因为需要产生一些动作,然后给调用者一些回调,所以用到了事件和监听器。
举个例子,比如DragListener和DragEvent,最开始写的时候,每次Drag动作都触发一个DragEvent事件,然后就得new一个DragEvent对象。后来感觉这样太浪费内存了,然后就研究了一下系统的MotionEvent这个类,找到了好的解决方案。
MotionEvent的构造方法是匿名的,不能直接创建,对外提供的获取对象的接口是静态的obtain方法,可以从一个MotionEvent对象获取,也可以从一些变量获取。为什么说它是个好的解决方案呢,因为它提供了一个recycle方法,可以将当前的对象回收,下次要用的时候就不用重新再new一个新的对象了,直接从它的回收池里面拿就行。
下面讲解一下,MotionEvent里面有几个比较重要的变量,如下 :
然后有一个静态的obtain方法:
其它几个obtain方法都首先调用obtain()方法从回收栈中获取对象,然后赋值。
它的recycle方法如下:
其它几个obtain方法都首先调用obtain()方法从回收栈中获取对象,然后赋值。
它的recycle方法如下:
根据这个思路,我也做了一个Event,同样的回收原理,使得事件触发频繁的时候,大大的节约了内存的使用