
Bitmap需调用 recycle() 是历史问题,在 Android 3.0之前,Bitmap 的图片数据是在底层C中处理的,因此在 Android3.0 之前 recycle() 是应该调用的。虽然 finalize() 会调用 recycle() ,但对Java有经验的同学应该知道只依靠 finalize() 去释放资源是会出很多问题的.
在Android 3.0之后,图片数据放在了Bitmap对象的一个成员变量 mBuffer[] 中。因此可以不调用recycle() .在 Bitmap 置 null 后图片数据会被GC回收。
现在都 Android5.0 的年代了,建议不考虑支持3.0之前的版本。
实际上Bitmap.recycle()的说明上也有说明:
This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
(这是一个高级函数,一般来说没必要调用。在没有引用指向 Bitmap 时,GC 会自动释放内存)
一般来说,如果代码已经不再需要使用Bitmap对象了,就可以释放了。释放内存以后,就不能再使用该Bitmap对象了,如果再次使用,就会抛出异常。所以一定要保证不再使用的时候释放。比如,如果是在某个Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中进行回收。
if(bitmap != null && !bitmap.isRecycled()){
// 回收并且置为null
bitmap.recycle();
bitmap = null;
}
System.gc();
1: Bitmap是否有调用recycle方法的必要性?
A: 嵌入式系统总是格外注重空间的问题,不小心的话就会有OOM。但是应用层使用java的android平台有其天然的优势【java语言有自己的垃圾回收,android平台上各个application有自己的process自己的空间】。
无需调用bitmap的理由有:
a. 垃圾回收会处理的;
b. 当application关闭,process被杀掉,所有这个process占用的空间自然回归系统;
但是,如果你有点洁癖,或者有点理想主义,或者很有控制欲,或者很闲。。。bitmap的recycle函数的调用还是可以是有必要的,理由有:
a. 垃圾回收虽然好使,但是有可能的话,我们还是让它少干点活吧。垃圾回收有很大的未来不确定性,会加重未来未知时间点的loading,若有大量bitmap需要垃圾回收处理,那必然垃圾回收需要做的次数就更多也发生地更频繁,小心会造成ANR。但是,若是自己recycle,就可以可控制地分散处理了这些回收任务了。
b. 若是launcher那样一直运行的application,它的process一直存在,memory问题还是多多注意下比较好。
Q2: When?
A: Timing的问题在这里很重要。早了就大事不好了,会有这样的Exception:
java.lang.RuntimeException,Canvas: trying to use a recycled bitmap
android.graphics.Bitmap@44ebeee0,Canvas.java,955
So, 怎样才可以保证不会早了呢?
关于图片显示,重要的时间点:
step1: 设置进去的时间点;
Step2: 画面画出来的时间点;
最保险最笨的做法,在新的图片设置进去以后再recycle掉老的图片,这样做的坏处在于,在某个时间段,你需要的空间是double的【新旧两套都在】;
如果你不偏向于那么做,又有时间,可以考虑后面一个时间点,除了setImage以及其它代码中显示调用那个bitmap的时候我们会检查bitmap,在acticvity变为visible的时候系统还是会去找之前设置进去的bitmap【即使你的onResume方法里面并没有提到去refresh UI,这件事情它也是会去做的,大概不然它就不知道这次该显示些什么了】。所以,在UI线程里面,在一个不可能被打断的方法里面,是先设置新的bitmap还是先recycle旧的图片是没有影响的。
譬如说 mBitmap.recycle();
mBitmap = ….. //设置
mImageView.setImage(mBitmap);
这样的代码是完全可以的。
后面这样的做法,最重要的就是确保:在UI线程【因为设置UI显示只能在UI主线程里】里面一个不可能被打断的方法里面。这个是为了确保在两者之间UI主线程不可能被打断,不可能刚好从invisible变成visible。
所以,特别小心两种东西:
1. 多线程【个人觉得最好不要在其他线程里面调用UI用过的bitmap的recycle方法,多线程之间是很难保证时间顺序的,暂时没有想出一种在background thread里面recycle的合理的方式】;
2. 非及时发生的方法:譬如,发intent啊,发notify啊去通知UI主线程去做UI重新刷新并不能替代mImageView.setImage(mBitmap);这样的句子。完全有可能,你确实发了intent出去了,但是目标activity之一还没有做UI重新设置【Q: maybe没收到 or 收到但还是等待处理,不确定这两种可能是不是都有可能】,这个时候这个acitivity变成visible了,系统仍然试图找旧的图片,找不到了就会报exception了。
PS: java.lang.RuntimeException,Canvas: trying to use a recycled bitmap android.graphics.Bitmap@44ebeee0,Canvas.java,955 这样的exception可能也许你并不能够看到,默认的log里面好像只能看到uncaught exception,第一次看到是在monkey的events.log里面,若你知道怎么打开相应手机这方面的log trace应该也是可以看到的。
