android BitmapFactory的OutOfMemoryError: bitmap size exceeds VM budget解决方案

    使用android提供的BitmapFactory解码一张图片时,有时会遇到该错误,即:java.lang.OutOfMemoryError: bitmap size exceeds VM budget。这往往是由于图片过大造成的。要想正常使用,一种方式是分配更少的内存空间来存储,即在载入图片的时候以牺牲图片质量为代价,将图片进行放缩,这也是不少人现在为避免以上的OOM所采用的解决方法。但是,这种方法是得不偿失的,当我们使用图片作为缩略图查看时候倒是没有说什么,但是,当需要提供图片质量的时候,该怎么办呢?java.lang.OutOfMemoryError: bitmap size exceeds VM budget着实让不少人欲哭无泪呀!前几天刚好有个需求需要载入SD卡上面的图片。

首先是使用

?
Bitmap bmp = BitmapFactory.decodeFile(pePicFile.getAbsolutePath() + "/" +info.getImage());

上面参数是我将要读取的图片文件及路径,当文件较小时,程序能够正常运行,但是当我选择一张大图时,程序立刻蹦出了java.lang.OutOfMemoryError: bitmap size exceeds VM budget的OOM错误!

在android设备上(where you have only 16MB memory available),如果使用BitmapFactory解码一个较大文件,很大的情况下会出现上述情况。那么,怎么解决?!

先说之前提到过的一种方法:即将载入的图片缩小,这种方式以牺牲图片的质量为代价。在BitmapFactory中有一个内部类BitmapFactory.Options,其中当options.inSampleSize值>1时,根据文档:

If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. (1 -> decodes full size; 2 -> decodes 1/4th size; 4 -> decode 1/16th size). Because you rarely need to show and have full size bitmap images on your phone. For manipulations smaller sizes are usually enough.

也就是说,options.inSampleSize是以2的指数的倒数被进行放缩。这样,我们可以依靠inSampleSize的值的设定将图片放缩载入,这样一般情况也就不会出现上述的OOM问题了。现在问题是怎么确定inSampleSize的值?每张图片的放缩大小的比例应该是不一样的!这样的话就要运行时动态确定。在BitmapFactory.Options中提供了另一个成员inJustDecodeBounds。

?
1 2 3 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true ; Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);

设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。Android提供了一种动态计算的方法。如下:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels); int roundedSize; if (initialSize <= 8 ) { roundedSize = 1 ; while (roundedSize < initialSize) { roundedSize <<= 1 ; } } else { roundedSize = (initialSize + 7 ) / 8 * 8 ; } return roundedSize; } private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { double w = options.outWidth; double h = options.outHeight; int lowerBound = (maxNumOfPixels == - 1 ) ? 1 : ( int ) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); int upperBound = (minSideLength == - 1 ) ? 128 : ( int ) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength)); if (upperBound < lowerBound) { return lowerBound; } if ((maxNumOfPixels == - 1 ) && (minSideLength == - 1 )) { return 1 ; } else if (minSideLength == - 1 ) { return lowerBound; } else { return upperBound; } }

以上参考一下,我们只需要使用此函数就行了:

?
BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true ; BitmapFactory.decodeFile(imageFile, opts); opts.inSampleSize = computeSampleSize(opts, - 1 , 128 * 128 ); //这里一定要将其设置回false,因为之前我们将其设置成了true opts.inJustDecodeBounds = false ; try { Bitmap bmp = BitmapFactory.decodeFile(imageFile, opts); imageView.setImageBitmap(bmp); } catch (OutOfMemoryError err) { }

这样,在BitmapFactory.decodeFile执行处,也就不会报出上面的OOM Error了。完美解决?如前面提到的,这种方式在一定程度上是以牺牲图片质量为代价的。如何才能更加优化的实现需求?

当在android设备中载入较大图片资源时,可以创建一些临时空间,将载入的资源载入到临时空间中。

?
1 2 BitmapFactory.Options bfOptions= new BitmapFactory.Options(); bfOptions.inTempStorage= new byte [ 12 * 1024 ];

以上创建了一个12kb的临时空间。然后使用Bitmap bitmapImage = BitmapFactory.decodeFile(path,bfOptions);但是我在程序中却还是出现以上问题!以下使用BitmapFactory.decodeFileDescriptor解决了以上问题:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 BitmapFactory.Options bfOptions= new BitmapFactory.Options(); bfOptions.inDither= false ; bfOptions.inPurgeable= true ; bfOptions.inTempStorage= new byte [ 12 * 1024 ]; // bfOptions.inJustDecodeBounds = true; File file = new File(pePicFile.getAbsolutePath() + "/" +info.getImage()); FileInputStream fs= null ; try { fs = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } Bitmap bmp = null ; if (fs != null ) try { bmp = BitmapFactory.decodeFileDescriptor(fs.getFD(), null , bfOptions); } catch (IOException e) { e.printStackTrace(); } finally { if (fs!= null ) { try { fs.close(); } catch (IOException e) { e.printStackTrace(); } } }

当然要将取得图片进行放缩显示等处理也可以在以上得到的bmp进行。

PS:请图片处理后进行内存回收bmp.recycle();这样将图片占有的内存资源释放。

更多相关文章

  1. android Gallery实现加载网络图片
  2. Android使用AsyncTask下载图片,最好使用WeakReference
  3. Android 打开相机、相册获取图片文件,支持Android 9.0系统
  4. android中调用系统功能 来显示本地相册图片 拍照 视频 音频功能
  5. Android中欢迎界面背景图片放大效果
  6. Android 最新获取手机内置存储大小,SD卡存储空间大小方法
  7. Android分享图片
  8. android JNI处理图片的例子
  9. Android仿微信朋友圈发图片和文字

随机推荐

  1. 新书内容连载(3):Android(安卓)SDK中常用命
  2. Android防止内存溢出浅析
  3. Android与IOS异同点对比(1)------ 显示
  4. 在Android中创建和使用数据库
  5. Android(安卓)release版本apk添加数字签
  6. Android(安卓)JNI 机制
  7. Android时区问题
  8. Android调用系统自带的文件管理器进行文
  9. 移动互联网的新宠:Android之缤纷世界
  10. Android仿腾讯视频悬浮窗的实现