专栏首页Android群英传Bitmap.recycle引发的血案

Bitmap.recycle引发的血案

从Bitmap.recycle说起

在Android中,Bitmap的存储分为两部分,一部分是Bitmap的数据,一部分是Bitmap的引用。

在Android2.3时代,Bitmap的引用是放在堆中的,而Bitmap的数据部分是放在栈中的,需要用户调用recycle方法手动进行内存回收,而在Android2.3之后,整个Bitmap,包括数据和引用,都放在了堆中,这样,整个Bitmap的回收就全部交给GC了,这个recycle方法就再也不需要使用了。

然而……

现在的SDK中对recycle方法是这样注释的,如图所示:

bitmap.png

可以发现,系统建议你不要手动去调用,而是让GC来进行处理不再使用的Bitmap。我们可以认为,即使在Android2.3之后的版本中去调用recycle,系统也是会强制回收内存的,只是系统不建议这样做而已。

鄙司代码有些是从Android2.3出来的,因此很多地方还在使用Bitmap.recycle。通常情况下,这也没什么问题,但是,今天遇到一个bug引发了Bitmap.recycle的血案。

起因

这个bug的起因是因为我们的一张图片需要旋转,同时可以设置一个旋转角度,老的代码是这样写的:

ImageView imageView = (ImageView) findViewById(R.id.test);
Matrix matrix = new Matrix();
matrix.setRotate(0.013558723994643297f);Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
Bitmap targetBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);if (!bitmap.isRecycled()) {
    bitmap.recycle();
}
imageView.setImageBitmap(targetBmp);

除了中间的0.013558723994643297f这串比较奇葩的数据(当然,正常情况下都是20、30这样正常的数),其它都是比较正常的代码。

但实际上,只要一运行这段代码,程序就会崩溃,错误原因如下所示:

E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: com.xys.preferencetest, PID: 30512
                  java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@1a50ff6b

这个问题一看就知道是由于Bitmap被调用recycle方法回收后,又调用了Bitmap的一些方法而导致的。可是,代码中可以发现我们recycle的是bitmap而不是通过Bitmap.createBitmap重新生成的targetBmp,为什么会报这个exception呢?

注释

按道理来说,bitmap与create出来的targetBmp应该是两个对象,当旋转角度正常的时候,确实也是这样,但当旋转角度比较奇葩的时候,这两个bitmap对象居然变成了同一个!而打开Bitmap.createBitmap的代码,可以发现如下所示的注释:

bitmap2.png

这里居然写着:The new bitmap may be the same object as source, or a copy may have been made.

看来还是真有可能为同一个对象的!

猜测

经过几次尝试,发现只有在角度很小很小的时候,才会出现这个情况,两个bitmap是同一个对象,因此,我只能这样猜测,当角度过小时,系统认为这是一张图片,没有发生变化,那么系统就直接引用同一个对象来进行操作,避免内存浪费。那么这个角度是怎么来的呢?继续猜测,如图所示:

bitmap3.png

当图像的旋转角度小余两个像素点之间的夹角时,图像即使选择也无法显示,因此,系统完全可以认为图像没有发生变化,因此,注释中的情况,是不是有可能就是说的这种情况呢?

我还没有来得及继续验证,希望大家可以一起讨论下~有说的不对的还请指教。

然而……

然而,教训是,在不兼容Android2.3的情况下,别在使用recycle方法来管理Bitmap了,那是GC的事!

本文分享自微信公众号 - Android群英传(android_heroes),作者:徐宜生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-01-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 深入Java源码解析容器类List、Set、Map

    用户1907613
  • Xposed加载JNI库

    用户1907613
  • 调教Android的打盹

    用户1907613
  • [2013.8.16]小议innerText/HTML以及outerText/HTML

    inner指的是Button1的部分,即只有文本。 outer指的是整个一行,包括标签。 那么Text和HTML呢? HTML是w3c标准,理论上所有浏...

    ApacheCN_飞龙
  • 演示视频:在K8S上备份和恢复MySQL

    这是关于PX-Backup的一个Demo。在左侧,有PX-Backup和两个集群。我们使用上面那个集群,运行的是1.17.8版本的K8S。在右侧有一个终端,也是...

    Portworx
  • 【ICLR2018 最高分论文】利用分布鲁棒优化方法应对对抗样本干扰

    【导读】近日,深度学习顶会ICLR2018评审结果出炉,得分最高的论文是 《Certifiable Distributional Robustness with...

    WZEARW
  • 自定View练习第一节

    项勇
  • 行业盘点 | 通信行业10大关键词

    前言 不知不觉2018年已经到来,回顾2017年,通信行业在机遇与挑战中砥砺前行:促进行业改革的新政纷纷颁布,让我们回顾下2017年通信行业的十大关键词吧。 1...

    灯塔大数据
  • 比 SharedPreferences 更高效?微信 MMKV 源码解析

    MMKV 是微信于 2018 年 9 月 20 日开源的一个 K-V 存储库,它与 SharedPreferences 相似,但又在更高的效率下解决了其不支持跨...

    glumes
  • 《科学》杂志:2017年世界十大科学突破!

    2017年即将过去,回收这一年,Science公布了他们心目中的十大科学突破,供大家参考: 1.双中子星合并 2017年8月17日,LIGO和Virgo在1.3...

    WZEARW

扫码关注云+社区

领取腾讯云代金券