Bitmap.recycle引发的血案

简介: 从Bitmap.recycle说起在Android中,Bitmap的存储分为两部分,一部分是Bitmap的数据,一部分是Bitmap的引用。

从Bitmap.recycle说起

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

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

然而……

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

这里写图片描述

可以发现,系统建议你不要手动去调用,而是让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的代码,可以发现如下所示的注释:

这里写图片描述

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

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

猜测

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

这里写图片描述

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

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

然而……

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

目录
相关文章
|
12月前
|
缓存 算法 Java
Bitmap引起的OOM问题
Out Of Memory(内存溢出),我们都知道Android系统会为每个APP分配一个独立的工作空间,或者说分配一个单独的Dalvik虚拟机,这样每个APP都可以独立运行而不相互影响!而Android对于每个Dalvik虚拟机都会有一个最大内存限制,如果当前占用的内存加上我们申请的内存资源超过了这个限制,系统就会抛出OOM错误!
103 0
使用Bitmap.createBitmap遇到的问题
使用Bitmap.createBitmap遇到的问题
374 0
|
程序员
Bitmap避免OOM
目录介绍 01.先看一个需求分析案例 02.Bitmap占用内存介绍 03.影响Bitmap占用内存因素 04.图像加载的方式 05.加载图像内存去哪里了 06.具体实现加载图片步骤 希望世麟兄弟母亲尽快好起来 和南尘(世麟)认识与网络,估计好多程序员都看过他的文章,我也是。
1049 0
|
Java Android开发
Bitmap详解
Bitmap的分析与使用 Bitmap的创建 创建Bitmap的时候,Java不提供new Bitmap()的形式去创建,而是通过BitmapFactory中的静态方法去创建,如:BitmapFactory.
2063 0
|
存储 算法 程序员
Bitmap 算法
位图算法,内存中连续的二进制位bit,用于对大量整型数据做去重和查询。 举个例子,给定一块长度是10bit的内存空间,依次插入4,3,2,1,怎么存储? 1. 给定长度是10的bitmap,每一个bit位分别对应着从0到9的10个整型数。
1504 0
|
Java
Bitmap获取图片内存溢出
从本地sd卡获取图片内存溢出解决方法 public Bitmap getBitmap(String path) { File file = new File(path); Bitmap resizeBmp = null; BitmapFactory.
826 0