解决内存溢出(OOM)-Bitmap.Factory压缩图片那些事

挥舞着指尖,谱写指尖的艺术

两种压缩图片的方式

  • Bitmap.Factory

  • bitmap.compress

阅读完本文你的收获

  1. Bitmap.Factory是怎样进行图片缩小的;
  2. 为什么bitmap.compress压缩图片了,还是造成了OOM;
  3. 怎样去正确的选择一个适合自己的压缩方式;

进入正题

1. ComPress图片质量压缩

我们在实际开发中,经常会用后台返回的url地址,进行图片的下载,或者说是调用相机拍摄照片时,图片都会异常的大,一张图片3M,4M,大量如此,OOM是在所难免的。

先上代码

    public static Bitmap simaplePic(Bitmap bitmap) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG,70,stream);
        stream.close();
        return bitmap;
    }

我们传入一个bitmap类型,通过

ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG,70,stream);

两行代码去执行图片的缩小,创建一个字节输出流,调用compress方法,三个参数分别对应:

  1. 图片后缀格式;
  2. 保留原始图片的70%质量;
  3. 字节输出流;

后缀格式不用说,第二个参数保 留原始图片的70%质量 ,意思是压缩了30%的质量。

  1. 这种压缩方法之所以称之为质量压缩,是因为它不会减少图片的像素。
  2. 它是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的。
  3. 进过它压缩的图片文件大小会有改变,但是导入成bitmap后占得内存是不变的。
  4. 因为要保持像素不变,所以它就无法无限压缩,到达一个值之后就不会继续变小了。
  5. 显然这个方法并不适用于缩略图,其实也不适用于想通过压缩图片减少内存,仅仅适用于想在保证图片质量的同时减少文件大小的情况而已。
2. Bitmap.Factory像素/采样率图片压缩

这种方式就会适用于缩略图,减少占用内存。我这里用的是后台给的url地址,下载图片。
铁汁 ~ 上代码

public class CompressPic {
    /**
     * 图片缩小工具类
     * @param stream 图片的字节
     * @param reqWidth 自定义想要缩小的宽
     * @param reqHeight 自定义想要缩小的高
     * @return
     */
    public static Bitmap decodeBitmap(byte[] stream, int reqWidth, int reqHeight){
        //获取当前图片的高
        int inPictureHeight;
        //获取当前图片的宽
        int inPictureWidth;
        //默认宽高都压缩一倍
        int inSample = 1;
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 设置为true时,bitmap = null,不加载进内存,但可以得到图片的宽高
        //只获取图片的大小信息,而不是将整张图片载入在内存中,避免内存溢出
        options.inJustDecodeBounds = true;
        //byte, byte.length, options
        BitmapFactory.decodeByteArray(stream, 0,stream.length, options);
        //获取图片资源的宽高
        inPictureHeight = options.outHeight;
        inPictureWidth = options.outWidth;
        //图片缩小算法
        BitmapFactory.Options resultOption = calculationWidthAndHeight(options,inPictureHeight,inPictureWidth,reqWidth,reqHeight,inSample);
        return BitmapFactory.decodeByteArray(stream,0,stream.length,resultOption);

    }

    private static BitmapFactory.Options calculationWidthAndHeight(BitmapFactory.Options options, int inPictureHeight, int inPictureWidth,
                                                                   int reqWidth, int reqHeight, int inSample) {
        //更改原始像素为reqWidth,reqHeight比例,如果原始像素比自定义像素要小,则跳过此步骤,都比自定义像素要小了,还压缩个啥
        if (inPictureWidth > reqWidth || inPictureHeight >reqHeight){
            while (inPictureWidth / inSample > reqWidth || inPictureHeight / inSample >reqHeight){
                //要求取值为n的2次冥,不是二次幂则四舍五入到最近的二次幂
                inSample = inSample *2;
            }
            Log.e("TAG",inSample+"");
        }
        //得到的最终值
        options.inSampleSize = inSample;
        //设置编码为RGB_565,默认为ARGB_8888
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        //设置为false,得到实际的bitmap,不然只会得到宽高等信息
        options.inJustDecodeBounds = false;
        //设置图片可以复用
        options.inMutable = true;
        return options;
    }
}

调用工具类

bitmap = CompressPic.decodeBitmap(response.body().bytes(),100,100);

主要用到的几个参数:

  • options.inJustDecodeBounds;
    该参数设置为true时,不加载图片资源进内存,意思是bitmap返回的是null,但是可以拿到图片的一些信息,比如它的宽高。

  • options.isSampleSize;
    设置要缩放的倍数,以n * 2开始扩展,n * 0.6这样的,默认四舍五入倍数,800 x 600像素,该值设置为2时,宽高默认都缩小两倍,即总体缩了4倍。

  • options.inPreferrConfig;
    设置图片编码格式,默认ARGB_888
    每个像素4字节,例如800 x 600,则字节位 800 x 600 x 4。
    RGB_565,每个像素2个字节,800 x 600 x2。

  • options.inMutable;
    图片复用, 不多做赘述。

总结

质量压缩:实质上没减少bitmap内存开销,只是图片文件大小变小了;
像素压缩:达到了内存压缩方式;

你可能感兴趣的:(Android开发旅途,android,java,android,studio)