android大图片转bitmap时造成内存溢出的一种解决方案

在将大图片转成bitmap时造成的内存溢出问题显然无法通过recycle(),软引用等方式来解决(才一个bitmap就溢出了)
  比较简单的折中方案就是采用BitmapFactory的内部类 Options来对图片进行压缩
 
  查阅源码javadoc Options类的公共属性inJustDecodeBounds的注释如下
        /**
         * If set to true, the decoder will return null (no bitmap), but
         * the out... fields will still be set, allowing the caller to query
         * the bitmap without having to allocate the memory for its pixels.
         */
明显 如果这个属性设置为true时 此时decode出来的bitmap是不予分配内存的 但是却可以取到bitmap的长宽
这时采用适当的比例对此长宽进行压缩 既可以将大图片转为bitmap在程序中显示

如下代码
                Options opts = new Options();
opts.inJustDecodeBounds = true;
String path = bmFile.getAbsolutePath();
BitmapFactory.decodeFile(path, opts);

int inSampleSize = (int) Math.ceil(Math.sqrt(opts.outWidth
* opts.outHeight / (width * height)));

opts.inJustDecodeBounds = false;
opts.inSampleSize =inSampleSize;
Log.d(TAG, "inSampleSize is" + inSampleSize);
Bitmap comBm = BitmapFactory.decodeFile(path, opts);
根据具体需求 只要 inSampleSize计算合理 完全可以最大限度保存图片质量

另外注意inSampleSize这个属性的javadoc
         Note: the decoder will try to fulfill this request, but the resulting bitmap
         may have different dimensions that precisely what has been requested.
         Also, powers of 2 are often faster/easier for the decoder to honor.
就是说 返回的图片尺寸未必与你要求的是一样的 而且取值为2的整数次幂是比较好的

关于 inSampleSize的算法 android.media包下ThumbnailUtils类提供2个算法 computeSampleSize和computeInitialSampleSize函数供android内部压缩图片时使用 坑爹的是这2个均为private方法 computeSampleSize会调用computeInitialSampleSize 最终返回一个Int值作为
inSampleSize使用 同时 该类中提供的createImageThumbnail压缩bitmap方法虽然为公共方法却神奇的不能调用(android坑爹的@hide注解) so 如果想使用这个算法 只有将2个函数copy到自己的代码中使用 否则可以根据要自己实现一个算法
   下边是自己实现的一个简单算法 根据想要的宽高与实际宽高对比 算出缩放的倍数 然后取与这个倍数最接近的2的整数次幂进行缩放 较大程度上保证与要求的目标宽高接近(在我的项目中使用实际和android算法提供的效果差不多)
      /**
* 取最接近参数的2的整数次幂
*
* @param inSampleSize
* @return
*/
private static int computeSampleSize(int inSampleSize) {
// 每次除2的商
int result = inSampleSize;
// 每次除以2的余数
int remainder = 0;
// 计数器
int i = 0;
// 参数是否就是2的整数次幂
boolean flag = true;
// 循环除以2 直到商为1时
while (result != 1) {
remainder = inSampleSize % 2;
if (remainder != 0) {
flag = false;
}
result = result / 2;
i++;
}
if (flag) {// 所有余数都为0 则参数即是2的整数次幂
return inSampleSize;
}
//取最接近的2的整数次幂
int outSampleSize = (int) (Math.abs(Math.pow(2, i) - inSampleSize) > Math
.abs(Math.pow(2, i + 1) - inSampleSize) ? Math.pow(2, i + 1)
: Math.pow(2, i));
// int outSampleSize = ( int ) Math.pow(2, i + 1);

return outSampleSize;
}

猜你喜欢

转载自ynky1026.iteye.com/blog/1740911