android 图片压缩小结

前言:目前,大多数的APP应用都需要在界面上展示、加载、存储、上传图片,所有,图片的使用可以说非常频繁和重要的。而为了提高加载效率、节省上传流量、减小内存占用等,我们基本上都是需要多图片进行压缩处理的,否则容易出现OOM异常。根据个人掌握的对图片压缩进行总结一二:
一、图片压缩类型及存在的几种形式:
(1)图片压缩一般可以分为:图片尺寸压缩和图片质量压缩,其中,质量压缩一般用于上传大图之前的处理,这样可以节省一定的流量;
(2)而总的来看,图片主要有三种存在形式:在本地或者硬盘上以file文件形式存储,网络传输时以stream流的形式存在,而内存中以stream或者Bitmanp形式存在。
二、图片尺寸压缩:
1)首先,看一下BitmapFactory这个类的部分源码:

public class BitmapFactory {
    public BitmapFactory() {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeFile(String pathName, BitmapFactory.Options opts) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeFile(String pathName) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeResourceStream(Resources res, TypedValue value, InputStream is, Rect pad, BitmapFactory.Options opts) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeResource(Resources res, int id, BitmapFactory.Options opts) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeResource(Resources res, int id) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options opts) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeStream(InputStream is) {
        throw new RuntimeException("Stub!");
    }

    public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, BitmapFactory.Options opts) {
        throw new RuntimeException("Stub!");
    }
    public static class Options {
        public Bitmap inBitmap;
        public boolean inJustDecodeBounds;
        public int inSampleSize;
        public int outHeight;
        public String outMimeType;
        public int outWidth; 
    }
}

其中,可以看出BitmapFactory这个类提供了多个解析方法如:decodeFile、decodeStream、decodeResource等用于构建Bitmanp对象,而我们在使用的时候是根据不同的情景来选择对应的解析方法获得Bitmap的,如本地SD卡中的图片可以使用decodeFile()方法,网络上的图片可以通过decodeStrean()方法,内存中的图片可以通过decodeStream()方法。通过这些方法获得Bitmap对象之后,默认情况下这些方法会为Bitmap对象分配内存,此时很容易出现OOM异常。针对这个缺陷,Google提供了另外一种解决方法:(也就是尺寸压缩的比较重要的依据)
在每一种获得Bitmap对象的方法中,都提供了一个可选的options参数,当我们将options参数的inJustDecodeBounds属性设置为true时,即可以禁止解析方法在构建的Bitmap对象时为其分配内存,返回值不再是Bitmap,而是null,但另一方面BitmapFactroy.options的outWith和outHeight、outMineType属性都已经被赋值了,根据这一特性,我们就可以在加载图片之前获取到图片的宽和高,进而根据实际情况对图片进行压缩:
压缩流程:
1)修改默认分配内存方法:

BitmapFactroy.Options options=new BitmapFatroy.Options();

options.inJustDecodeBounds=true;

int  imageHeight=options.outHeight;

int  imageWidth=options.outWidth;

String imageType=options.outMimeType;

2)计算合理的inSampleSize值:
根据下面的方法,可以算算出合理的inSampleSize值,而这个值正是我们需要确定的。

public static int caculateInSamepleSize(BitmapFactroy.Options options,int width,int height){

        //获取源图片宽高
        final int height=option.outHeight;
        final int width=option.outWidth;

        int inSampleSize=0;
        if(height>reqHeight||width>reqWidth){
            //计算出实际宽高和目标宽高的比率
            final int heightRatio=Math.round((float)height/(float)reqHeight);
            final int widthRatio=Math.round((float)width/(float)reqHeight);
            //选中宽高最小比率作为inSampleSize 值,这样可以保证最终图片的宽和高一定大于等于目标图片的宽和高
            inSampleSize=heightRatio<heightRatio?heightRatio:widthRatio
        }

        return inSampleSize;

            }

3)获得压缩的Bitmap对象:

public static Bitmap decodeSampleBitmapFromResurce(Resource res,int resId,int  reqHeight,int reqWidth){


                BitmapFactroy.Options options=new BitmapFatroy.Options();
                //第一次解析将inJustDecodeBounds设为true,表示不再为Bitmap分配内存,同时获取图片宽和高
                options.inJustDecodeBounds=true;

                BitmapFactroy.decodeSampleBitmapFromResurce(res,resId,options);
                //调用 caculateInSamepleSize()方法的inSampleSize值
                options.inSampleSize=caculateInSamepleSize(options,reqHeight,reqWidth);
                //第二次解析,将inJustDecodeBounds设置为false,为Bitmap分配内存并获取Bitmap对象
                options.inJustDecodeBounds=false;
                //返回经过压缩的Bitmap对象
                return  BitmapFactroy.decodeSampleBitmapFromResurce(res,resId,options);


            }

首先,需要将BitmapFactroy.Options的inJustDecodeBounds属性设置为true,解析一次图片;紧接着,将BitmapFactroy.Options 连同期望的宽高传入caculateInSamepleSize()方法中从而获取到一个比较合理的 inSampleSize值;最后将inJustDecodeBounds设置为false,同时根据获取到的inSampleSize值再次解析图片,得到压缩之后的Bitmap对象。

三、质量压缩:
所谓的质量压缩,它其实只能实现对file的影响,可以把一个file转成bitmap再转成file,或者直接将一个bitmap转成file时,这个最终的file是被压缩过的,但是中间的bitmap并没有被压缩(事实上其内部是否没有被压缩我并不确定,只是根据Bitmap大小没有变化来判断的),因为bitmap在内存中的大小是按像素计算的,也就是width * height,对于质量压缩,并不会改变图片的像素,所以就算质量被压缩了,但是bitmap在内存的占有率还是没变小,但你做成file时,它确实变小了。
其中的原因,网上一些资料指出的是:这个压缩过程中,会让图片产生重构,有可能是图像的色深(也叫位深)和每一个像素的透明度发生了变化。还有一点值得注意的是:JPEG格式的图片压缩之后,原图中的透明元素将会消失,所以可能会造成失真。

public static void compressBitmapToFile(Bitmap bitmap,File file){

            ByteArrayOutPutStream baos=new ByteArrayOutPutStream();
            //options可以随意设定
            int options=80;
            //调用Bitmap的 compress()方法进行压缩
            bitmap.compress(Bitmap.CompressFormat.JPES,Options options);
            //根据压缩要求,逐次递减 options,直到符合要求,最后将File文件进行输出
            while(baos.toByteArray().length/1024>100){

            baos.reset();

            options--=10;

            bitmap.compress(Bitmap.CompressFormat.JPES,Options options);

            }

            try{
            FileOutPutStream fileOutPutStream = new FileOutPutStream(file);

            fileOutPutStream.write(baos.toByteArray())

            fileOutPutStream.flush();

            fileOutPutStream.close();

            }
            catch(Exception e){

            e.printStackTrace();


            }

总的来说,质量压缩就是根据File文件的大小,将Bitmap或者Stream形式的图片压缩成File形式。

以上,基本是图片尺寸、质量压缩的整体技术点,其中某些地方有可能阐述的不是太准确,欢迎大神指出。

猜你喜欢

转载自blog.csdn.net/ygz111111/article/details/80937477