4

Bitmap.recycle引发的血案

 3 years ago
source link: https://blog.csdn.net/eclipsexys/article/details/50581162
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

从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的事!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK