在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报错分析相关推荐

  1. Android Studio打不开,出现start failed的报错

    Android Studio打不开,出现start failed的报错 java.util.concurrent.CompletionException: java.lang.IllegalState ...

  2. 微信昵称emoji表情,特殊表情导致列表不显示,导出EXCEL报错等问题解决!

    微信昵称emoji表情,特殊表情导致列表不显示,导出EXCEL报错等问题解决! 参考文章: (1)微信昵称emoji表情,特殊表情导致列表不显示,导出EXCEL报错等问题解决! (2)https:// ...

  3. 【Bitmap】Canvas: trying to use a recycled bitmap android.graphics.Bitmap问题

    Canvas: trying to use a recycled bitmap android.graphics.Bitmap问题 我这用到bitmap中间变量了,还用到 Bitmap bitmap ...

  4. 【Android 安全】DEX 加密 ( Proguard 混淆 | 混淆后的报错信息 | Proguard 混淆映射文件 mapping.txt )

    文章目录 一.Proguard 混淆后的报错信息 二.Proguard 混淆映射文件 mapping.txt 更多 ProGuard 混淆配置参考 : https://www.guardsquare. ...

  5. 多线程中使用UNITY变量导致线程执行断掉却又不报错的问题

    时间:2020.9.29,XMoba第一次Demo 子线程调用UNITY的相关变量或函数导致程序执行过程断掉,且不报任何错误的一个BUG 一,问题现象 网络异步连接的回调函数中使用了一个函数XLog. ...

  6. Android开发之The application could not be installed: INSTALL_FAILED_VERSION_DOWNGRADE报错

    关键报错信息:The device already has a newer version of this application. 出现这个情况一般是因为已经安装过app,存在相同的APP了,或者你 ...

  7. Android彻底解决Youtube和Google play store等套件报错崩溃的问题

    做系统定制的同学可能会遇到第三方的软件比如Google Play store套件或者Youtube报错的问题,常常我们知道报错是因为什么但是就是没法改,有一种种很极端的方法就是像我这样直接把报错的地方 ...

  8. Android Selinux avc报错分析

    avc权限报错 E SELinux : avc: denied { find } for interface=vendor.qti.hardware.perf::IPerf sid=u:r:media ...

  9. android百度地图 okhttp,阳光沙滩-Android8.0用OkHttp3报错,而andoird9.0和10.0不报错

    仅管报错,但程序仍然能正常运行. 用红线标出来的是它认为有错的地方 2020-03-14 12:33:18.747 5759-5759/com.cxb.webshop I/zygote: Reject ...

  10. Android S内置APK时AndroidManifest使用uses-library编译报错

    (1)安装或编译出现的错误 Google关于这方面在Android S的改动有文档输出,可以参考如下:Dexpreopt 和 uses-library 检查. 此项报错主要是构建系统在Android. ...

最新文章

  1. ubuntu9.10 qq自动退出修复
  2. VxWorks6.6 pcPentium BSP 使用说明(二):创建启动盘
  3. think in java i o_《Thinking in Java》学习——18章Java I/O系统(三)
  4. 「Python基础知识」Python字符串是什么,如何使用
  5. BAPI:BAPI_PRODORDCONF_CREATE_TT (TCODE:CO11N)
  6. can接收进入两次中断_STM32的CAN2口无法进入接收中断
  7. 磁盘类型 GetDriveType
  8. 释放空间后将指针置空
  9. u盘用bitlocker加密后无法读取访问怎么办?
  10. 批量将txt文件转为csv文件
  11. 大数据给交通行业带来的五大变革
  12. SQL SERVER 远程主机强迫关闭一个现有连接
  13. 买服务器挂网站吗,云服务器 挂网站吗
  14. 如何设置 Mac 键盘的打字音效?
  15. html让gif图片暂停,控制GIF动画暂停播放的代码
  16. 基于计算机视觉的智能交通监控系统
  17. 分享给大家几个好玩的网站
  18. 【压缩感知合集1】(背景知识)香农奈奎斯特采样定理的数学推导和图解分析
  19. Linux 搭建本地镜像源(CentOS 离线 yum)
  20. 微信分账支付和退款资金流动

热门文章

  1. Hidistro 易分销2.0 源码带注释非反编译源码真正完整版本
  2. 2022年国内各安卓应用市场上传教程
  3. php外包如何逃离垃圾客户案例(转)
  4. WebRTC回声消除(1)
  5. 透过 AI 技术解读人的行为 研究开发回声定位
  6. 项目成败的关键要素:有效沟通
  7. MySql 8.0对应的驱动包
  8. html页面大于号,css中大于号()是什么意思?
  9. 修复dhcp client服务器,无法开启DHCP Client服务解决方法
  10. 更好的Google Glass:棱镜变长、Intel Atom处理器和外置电池组