Android bitmap.recycle()导致trying to use a recycled bitmap报错分析
在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" ><ImageViewandroid: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 resourcesif ((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()过,应该从这里分析具体原因。
Android bitmap.recycle()导致trying to use a recycled bitmap报错分析相关推荐
- Android Studio打不开,出现start failed的报错
Android Studio打不开,出现start failed的报错 java.util.concurrent.CompletionException: java.lang.IllegalState ...
- 微信昵称emoji表情,特殊表情导致列表不显示,导出EXCEL报错等问题解决!
微信昵称emoji表情,特殊表情导致列表不显示,导出EXCEL报错等问题解决! 参考文章: (1)微信昵称emoji表情,特殊表情导致列表不显示,导出EXCEL报错等问题解决! (2)https:// ...
- 【Bitmap】Canvas: trying to use a recycled bitmap android.graphics.Bitmap问题
Canvas: trying to use a recycled bitmap android.graphics.Bitmap问题 我这用到bitmap中间变量了,还用到 Bitmap bitmap ...
- 【Android 安全】DEX 加密 ( Proguard 混淆 | 混淆后的报错信息 | Proguard 混淆映射文件 mapping.txt )
文章目录 一.Proguard 混淆后的报错信息 二.Proguard 混淆映射文件 mapping.txt 更多 ProGuard 混淆配置参考 : https://www.guardsquare. ...
- 多线程中使用UNITY变量导致线程执行断掉却又不报错的问题
时间:2020.9.29,XMoba第一次Demo 子线程调用UNITY的相关变量或函数导致程序执行过程断掉,且不报任何错误的一个BUG 一,问题现象 网络异步连接的回调函数中使用了一个函数XLog. ...
- Android开发之The application could not be installed: INSTALL_FAILED_VERSION_DOWNGRADE报错
关键报错信息:The device already has a newer version of this application. 出现这个情况一般是因为已经安装过app,存在相同的APP了,或者你 ...
- Android彻底解决Youtube和Google play store等套件报错崩溃的问题
做系统定制的同学可能会遇到第三方的软件比如Google Play store套件或者Youtube报错的问题,常常我们知道报错是因为什么但是就是没法改,有一种种很极端的方法就是像我这样直接把报错的地方 ...
- Android Selinux avc报错分析
avc权限报错 E SELinux : avc: denied { find } for interface=vendor.qti.hardware.perf::IPerf sid=u:r:media ...
- android百度地图 okhttp,阳光沙滩-Android8.0用OkHttp3报错,而andoird9.0和10.0不报错
仅管报错,但程序仍然能正常运行. 用红线标出来的是它认为有错的地方 2020-03-14 12:33:18.747 5759-5759/com.cxb.webshop I/zygote: Reject ...
- Android S内置APK时AndroidManifest使用uses-library编译报错
(1)安装或编译出现的错误 Google关于这方面在Android S的改动有文档输出,可以参考如下:Dexpreopt 和 uses-library 检查. 此项报错主要是构建系统在Android. ...
最新文章
- ubuntu9.10 qq自动退出修复
- VxWorks6.6 pcPentium BSP 使用说明(二):创建启动盘
- think in java i o_《Thinking in Java》学习——18章Java I/O系统(三)
- 「Python基础知识」Python字符串是什么,如何使用
- BAPI:BAPI_PRODORDCONF_CREATE_TT (TCODE:CO11N)
- can接收进入两次中断_STM32的CAN2口无法进入接收中断
- 磁盘类型 GetDriveType
- 释放空间后将指针置空
- u盘用bitlocker加密后无法读取访问怎么办?
- 批量将txt文件转为csv文件
- 大数据给交通行业带来的五大变革
- SQL SERVER 远程主机强迫关闭一个现有连接
- 买服务器挂网站吗,云服务器 挂网站吗
- 如何设置 Mac 键盘的打字音效?
- html让gif图片暂停,控制GIF动画暂停播放的代码
- 基于计算机视觉的智能交通监控系统
- 分享给大家几个好玩的网站
- 【压缩感知合集1】(背景知识)香农奈奎斯特采样定理的数学推导和图解分析
- Linux 搭建本地镜像源(CentOS 离线 yum)
- 微信分账支付和退款资金流动