Android开发中处理图片OOM的若干方法小结
?
演示结果与说明
1.演示一
首先采用最简单的图片加载方式,不带任何图片缓存、调整大小或者回收,SimpleImageLoader.class便是承担此职责。加载图片部分的代码如下:
@Override
public Bitmap loadBitmapImage(String path) {
?????? return BitmapFactory.decodeFile(path);
}
@Override
public Drawable loadDrawableImage(String path) {
?????? return new BitmapDrawable(path);
}
演示结果:在模拟器上图片只能加载1-3张,之后便会出现OOM错误;在Defy上不会出现错误;原因是两者内存限制不同,Defy上运行的是第三方ROM,内存分配有40MB。另外gallery每次显示一张图片时,都要重新解析获得一张图片,尽管在Defy上还未曾出错,但当图片量加大,GC回收不及时时,还是有可能出现OOM。
2.演示二
为图片加载的添加一个软引用缓存,每次图片从缓存中获取图片对象,若缓存中不存在,才会从Sdcard加载图片,并将该对象加入缓存。同时软引用的对象也有助于GC在内存不足的时候回收它们。ImageLoaderWithCache.class负责这个职责,关键代码如下:
private HashMap<String, SoftReference<Bitmap>> mImageCache;
?????? @Override
?????? public Bitmap loadBitmapImage(String path) {
?????? ?????? if(mImageCache.containsKey(path)) {
?????? ?????? ?????? SoftReference<Bitmap> softReference = mImageCache.get(path);
?????? ?????? ?????? Bitmap bitmap = softReference.get();
?????? ?????? ?????? if(null != bitmap)
?????? ?????? ?????? ?????? return bitmap;
?????? ?????? }
?????? ?????? Bitmap bitmap = BitmapFactory.decodeFile(path);
?????? ?????? mImageCache.put(path, new SoftReference<Bitmap>(bitmap));
?????? ?????? return bitmap;
?????? }
?????? @Override
?????? public Drawable loadDrawableImage(String path) {
?????? ?????? return new BitmapDrawable(loadBitmapImage(path));
?????? }
演示结果:在模拟器上,能不无缓存时多加载1-2张图片,但还是会出现OOM;在Defy上不曾出错。由于本次所用的图片都相对比较占内存,在GC还未来得及回收软引用对象时,就又要申请超出剩余量的内存空间,因此仍然没能完全避免OOM。如果换成加载大量的小图片,比如100*100规格的,缓存中软引用的作用可能就发挥出来了。(这一假设可以进一步试验证明一下)
3.演示三
为了进一步避免OOM,除了缓存,还可以对图片进行压缩,进一步节省内存,多数情况下调整图片大小并不会影响应用的表现力。ImageLoaderWithScale.class便是负责这个职责,调整大小的代码如下:
BitmapFactory.Options options = new BitmapFactory.Options();
?????? options.inJustDecodeBounds = true;
?????? BitmapFactory.decodeFile(path, options);
?????? if (options.mCancel || options.outWidth == -1 || options.outHeight == -1) {
?????? ?????? Log.d(“OomDemo”, “alert!!!” + String.valueOf(options.mCancel) + ” ” + options.outWidth + options.outHeight);
?????? ?????? return null;
?????? }
?????? options.inSampleSize = Util.computeSampleSize(options, 600, (int) (1 * 1024 * 1024));
?????? Log.d(“OomDemo”, “inSampleSize: ” + options.inSampleSize);
?????? options.inJustDecodeBounds = false;
?????? options.inDither = false;
?????? options.inPreferredConfig = Bitmap.Config.ARGB_8888;
?????? Bitmap bitmap = BitmapFactory.decodeFile(path, options);
演示结果:在上述代码中,首先解码图片的边界,在不需要得到Bitmap对象的前提下就能获得图像宽高(宽高值分别被设置到options.outWidth和options.outHeight两个属性中)。computeSampleSize这个方法的参数分别为“解析图片所需的BitmapFactory.Options”、“调整后图片最小的宽或高值”、“调整后图片的内存占用量上限”。结合原始图片的宽高,此方法可以计算得到一个调整比例,再用此比例调整原始图片并加载到内存中,此时图片所消耗的内存不会超出事先指定的大小。在模拟器中,限制图片所占内存大小为1*1024*1024时,比未压缩过时能加载更多图片,但仍然会出现OOM;若限制图片所占内存大小为0.5*1024*1024,则能完整的载入所有图片。所以调整图片大小还是能够有效节省内存的。在Defy中不会出错,原因同上。
4.演示四
在有些情况下,严重缩小图片还是会影响应用的显示效果的,所以有必要在尽可能少地缩小图片的前提下展示图片,此时手动去回收图片就变得尤为重要。在类ImageLoaderWithRecyle.class中,便增加了回收图片资源的方法:
@Override
?????? public void releaseImage(String path) {
?????? ?????? if(mImageCache.containsKey(path)) {
?????? ?????? ?????? SoftReference<Bitmap> reference = mImageCache.get(path);
?????? ?????? ?????? Bitmap bitmap = reference.get();
?????? ?????? ?????? if(null != bitmap) {
?????? ?????? ?????? ?????? Log.d(“OomDemo”, “recyling ” + path);
?????? ?????? ?????? ?????? bitmap.recycle();
?????? ?????? ?????? }
?????? ?????? ?????? mImageCache.remove(path);
?????? ?????? }
?????? }
演示结果:图片压缩限制仍然维持在1*1024*1024,在adapter中,及时调用releaseImage方法,回收暂时不需要的图片。此时模拟器中也从未出现过OOM,所以总的来讲,综合缓存、调整大小、回收等各种手段,还是能够有效避免OOM的。
小结
本文介绍了软引用缓存、调整大小、回收等手段来避免OOM,总体来说效果还是明显的。但实际应用场景中,图片的应用不想本文所演示的那样简单,有时候图片资源可能来自与网络,这时需要配合异步加载的方式先下载图片并通过回调的方法来显示;有时候图片资源还需要加边框、加文字等额外修饰,所以在图片加载之后还要另做处理。
另外由于本人能力所限以及时间关系,本文还有诸多不完善之处。比如对Android内存分配的理解不深,没能透彻地解释Bitmap的内存占用情况;通过自定义堆内存分配大小,优化Dalvik虚拟机的堆内存分配的方法来解决OOM,本文也没有给予演示;再比如在上文的演示试验里,没有把内存占用情况的详细信息用图像形式直观地展示出来;还有演示所用的图片数量过少、规格单一、测试环境偏少,所有没能进行更加严谨科学的对比试验,遗漏了某些意外情况。最后欢迎大家来共同探索、交流并提出建议。
参考代码
http://longerian.googlecode.com/svn/trunk/OomDemo
补充说明
本文原本是发表在本人的原生Wordpress空间里,无奈在天朝无法访问,所以特地搬家到此。
本文参考过的文章资料汇总
http://winuxxan.blog.51cto.com/2779763/512180
http://www.7dot9.com/2010/08/android-bitmap%E5%86%85%E5%AD%98%E9%99%90%E5%88%B6/
http://blog.csdn.net/rickleaf/article/details/6393185
http://hi.baidu.com/455611934/blog/item/cb56aa442bc7b829879473d8.html
http://ck19860613.iteye.com/blog/842732
http://blog.csdn.net/kavendb/article/details/5935577
?