Android bitmap.recycle()导致trying to use a recycled bitmap报错分析
来源:互联网 发布:电脑检测软件大全 编辑:程序博客网 时间:2024/05/16 14:51
在android实际项目中,有时会在Activity的onDestroy()做一些资源释放工作,比如bitmap资源。通常的写法是这样
public class NextActivity extends Activity {private ImageView imageView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_next);}@Overrideprotected void onDestroy() {if (imageView instanceof ImageView) {Drawable d = imageView.getDrawable();if (d != null && d instanceof BitmapDrawable) {Bitmap bmp = ((BitmapDrawable) d).getBitmap();bmp.recycle();bmp = null;}imageView.setImageBitmap(null);imageView.setBackgroundDrawable(null);if (d != null) {d.setCallback(null);}}System.gc();super.onDestroy();}}
对应的xml是这样
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".NextActivity" > <ImageView android:id="@+id/imageview" android:layout_width="wrap_content" android:layout_height="wrap_content"android:src="@drawable/ic_launcher" /></RelativeLayout>我们这里模拟启动Activity操作,MainActivity启动NextActivity,第一次启动正常,按back键,第二次启动,就会抛出java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@cd46a67。报错信息很明确,试图使用一个已经回收的bitmap。 我们log一下NextActivity的onCreate()方法,打印bitmap的哈希码值
第一次和第二次的实例为什么是相同的呢?每次onCrate()不是应该重新绘制吗?为什么相同呢?其实这是android的优秀设计,我们这里的bitmap使用xml的src来指定的Drawable,Android系统每次解析图片优先于从缓存中拿,没有才去创建,所以第一次是创建的实例,第二次是从缓存中拿到的数据。为了刨根问底,我们看一下源码。
我们知道xml给控件设置属性最终都是使用pull解析在用代码创建,那么我们应该看一下ImageView的构造方法
ImageView.class
public ImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initImageView(); TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ImageView, defStyle, 0); Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src); if (d != null) { setImageDrawable(d); } <span style="white-space:pre"></span>...... a.recycle(); //need inflate syntax/reader for matrix }我们重点看第8行,getDrawalbe(com.android.internal.R.styleable.ImageView_src);点进去
public Drawable getDrawable(int index) { final TypedValue value = mValue; if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (false) { System.out.println("******************************************************************"); System.out.println("Got drawable resource: type=" + value.type + " str=" + value.string + " int=0x" + Integer.toHexString(value.data) + " cookie=" + value.assetCookie); System.out.println("******************************************************************"); } return mResources.loadDrawable(value, value.resourceId); } return null; }这里13行装载Drawable,这个方法是Resources的,继续点进去。
/*package*/ Drawable loadDrawable(TypedValue value, int id) throws NotFoundException {...... Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key); if (dr != null) { return dr; } Drawable.ConstantState cs; if (isColorDrawable) { cs = sPreloadedColorDrawables.get(key); } else { cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key); } if (cs != null) { dr = cs.newDrawable(this); } else { if (isColorDrawable) { dr = new ColorDrawable(value.data); } if (dr == null) { if (value.string == null) { throw new NotFoundException( "Resource is not a Drawable (color or path): " + value); } String file = value.string.toString(); if (TRACE_FOR_MISS_PRELOAD) { // Log only framework resources if ((id >>> 24) == 0x1) { final String name = getResourceName(id); if (name != null) android.util.Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id) + ": " + name + " at " + file); } } if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file); if (file.endsWith(".xml")) { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file); try { XmlResourceParser rp = loadXmlResourceParser( file, id, value.assetCookie, "drawable"); dr = Drawable.createFromXml(this, rp); rp.close(); } catch (Exception e) { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); NotFoundException rnf = new NotFoundException( "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id)); rnf.initCause(e); throw rnf; } Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } else { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file); try { InputStream is = mAssets.openNonAsset( value.assetCookie, file, AssetManager.ACCESS_STREAMING); // System.out.println("Opened file " + file + ": " + is); dr = Drawable.createFromResourceStream(this, value, is, file, null); is.close(); // System.out.println("Created stream: " + dr); } catch (Exception e) { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); NotFoundException rnf = new NotFoundException( "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id)); rnf.initCause(e); throw rnf; } Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } } } } if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); cs = dr.getConstantState(); if (cs != null) { if (mPreloading) { final int changingConfigs = cs.getChangingConfigurations(); if (isColorDrawable) { if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) { sPreloadedColorDrawables.put(key, cs); } } else { if (verifyPreloadConfig(changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) { if ((changingConfigs&LAYOUT_DIR_CONFIG) == 0) { // If this resource does not vary based on layout direction, // we can put it in all of the preload maps. sPreloadedDrawables[0].put(key, cs); sPreloadedDrawables[1].put(key, cs); } else { // Otherwise, only in the layout dir we loaded it for. final LongSparseArray<Drawable.ConstantState> preloads = sPreloadedDrawables[mConfiguration.getLayoutDirection()]; preloads.put(key, cs); } } } } else { synchronized (mAccessLock) { //Log.i(TAG, "Saving cached drawable @ #" + // Integer.toHexString(key.intValue()) // + " in " + this + ": " + cs); if (isColorDrawable) { mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); } else { mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); } } } } } return dr; }
这里的代码较多,做了一些删节,只留下重点部分,方面阅读,可以看到第6行是先从缓存中拿Drawable,然后第20行else才是真正创建Drawable的地方,50行是pull解析xml的地方90行就放入了缓存,其中sPreloadedDrawables和mDrawableCache是LongSparseArray<Drawable.ConstantState>[]该类就是对HashMap的优化类,可以当作HashMap来使用,这样就不难理解,重新创建Bitmap的时候是同一个实例了,好了,明白了原因。做一个小结吧!
总结:
1.通过XML给控件设置的Drawable最好不要recycle(),除非该Drawable只使用一次。
2.android系统会将使用过的资源(R.Drawable)会放入缓存中,优化下次使用的速度。
3.出现trying to use a recycled bitmap报错的原因,就是使用的相同的实例,并且之前recycle()过,应该从这里分析具体原因。
1 0
- Android bitmap.recycle()导致trying to use a recycled bitmap报错分析
- Bitmap的recycle后Canvas: trying to use a recycled bitmap android.graphics.Bitmap问题
- 解决Bitmap recycle异常:Canvas: trying to use a recycled bitmap android.graphics.Bitmap
- 关于bitmap recycle trying to use a recycled bitmap android.graphics.Bitmap
- Android Bitmap使用recycle()后 报:trying to use recycled bitmap 的问题
- trying to use a recycled bitmap分析
- java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap:报错解决
- Canvas: trying to use a recycled bitmap android.graphics.Bitmap
- Canvas: trying to use a recycled bitmap android.graphics.Bitmap
- Android回收图片的bitmap,导致的Canvas: trying to use a recycled bitmap异常处理
- 分析Canvas: trying to use a recycled bitmap android.graphics.Bitmap@84709c2
- trying to use a recycled bitmap解决之道
- androird textview trying to use a recycled bitmap android.graphics
- Android Bitmap回收异常:Canvas: trying to use a recycled bitmap android.graphics.Bitmap解决
- 【Bitmap】Canvas: trying to use a recycled bitmap android.graphics.Bitmap问题
- Bitmap回收异常:trying to use a recycled bitmap android.graphics.Bitmap问题解决
- 今天遇到Canvas: trying to use a recycled bitmap android.graphics.Bitmap问题
- 使用高德地图时出现 trying to use a recycled bitmap android.graphics.Bitmap
- IntelliJ IDEA WEB项目的部署配置
- 让生命恢复纯净
- 2015年中国“互联网+”体育研究
- Arduino - 超声波测距
- Objective-C-语法积累
- Android bitmap.recycle()导致trying to use a recycled bitmap报错分析
- VNX5700_DR Slot 2: The DNS client is unable to connect to name server
- Java 文件输入、复制
- 学习 PipedInputStream PipedOutputStream 改进版
- 管式超滤系统:超滤技术在工业废水处理中的应用
- 用户角色管理
- IKEv1主模式证书协商中对证书相关载荷的处理。
- I2C Tools 学习笔记
- 如何让textarea不可拖拽