今天就来聊一聊Android中图片压缩的问题,Android中图片以Bitmap位图来占用内存,要想知道图片压缩,就得先知道Bitmap是如何占用内存的,下面就一步一步来讲解。
Bitmap占用内存大小 = 图片宽 * 图片高 * 图片每个像素所占用的字节数。
图片的宽高我们都知道,可是每个像素所占用的字节数什么鬼?Android中图片的压缩格式一般有一下几种:
ARGB_8888:分别代表Alpha透明的、Red、Green、Blue,后面四个8表示ARGB分别占8位,也就是4个字节,那么最终就可以理解为ARGB_8888格式的图片每个像素占用4个字节。
ARGB_4444:跟ARGB_8888相似,只不过每个占用4位,也就是2个字节(因为太模糊现在基本不用ARGB_4444格式图片)。
RGB_565:分别代表Red、Green、Blue,同样后面565代表RGB跟别占5、6、5位,也就是2个字节。
ALPHA_8:表示一个像素占8位,一个字节,没有颜色,只有透明的。
好了,上面我们大致介绍了Android中图片的几种压缩格式,其实Andorid默认的图片压缩格式是ARGB_8888,下面我们来看看,一张ARGB_8888的图片占用的内存,我随便网上下了一张图片,存在assets文件夹中读取出来,避免存在hdpi,xhdpi等,他们会默认缩放图片大小,再此不做详细赘述。
上图左边的是图片占用的内存字节数,右边是这张图片的宽高,默认ARGB_8888。1000 * 666 * 4=?,不就是2664000字节么。既然知道bitmap在内存中怎么占用内存了,下面就开始介绍图片压缩方法:
1、质量压缩
public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); //质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 image.compress(Bitmap.CompressFormat.JPEG, 100, baos); int options = 100; //循环判断如果压缩后图片是否大于300kb,小于继续压缩 while ( baos.toByteArray().length / 1024 > 300) { baos.reset();//重置baos即清空baos //这里压缩options%,把压缩后的数据存放到baos中 image.compress(Bitmap.CompressFormat.JPEG, options, baos); options -= 10;//每次都减少10 } //把压缩后的数据baos存放到ByteArrayInputStream中 ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); //把ByteArrayInputStream数据生成图片 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); return bitmap; }
上面这段代码的大致意思就是先构建一个字节数组输出流,自带缓冲区,先利用compress方法将bitmap位图读到缓冲区中,然后判断缓冲区中的内容字节数/1024也就是KB,是否大于300K,如果大于300的话则清除掉缓冲区的内容,继续按照压缩比例options读入缓冲区中,如此循环直到图片不大于300K则退出循环,返回Bitmap对象。其实关键就是通过设置options的大小来压缩图片,options取值是0-100,100表示不压缩。
上图是我打印的options的取值0-100个别数字,得到的结果确是占用的内存空间一样多的字节,没压缩啊感觉,为什么呢?解释一下,因为android所谓的质量压缩,它其实是压缩了图片的深度跟透明度,图片的宽高跟像素并没有改变,所以它所占用的内存空间并没有改变,但是质量压缩后的图片导出到手机本地会发现文件变小了,所以说android中质量压缩不适用于app浏览显示高清大图,个人感觉适用于上传给服务器批量大图文件,可以先利用质量压缩将压缩后的图片存储到本地,然后上传本地图片至服务器。这里顺便提下一下各位读者,质量压缩无法压缩png图片,因为png图片就是无损的,不能进行质量压缩。
2、采样率压缩
public static Bitmap compressImage(String srcPath) { BitmapFactory.Options newOpts = new BitmapFactory.Options(); //开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);//此时返回bm为空 newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; float hh = 800;//这里设置高度为1280f float ww = 400f;//这里设置宽度为720f //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 int be = 1;//be=1表示不缩放 if (w >= h && w > ww) {//如果宽度大的话根据宽度固定大小缩放 be = (int) (newOpts.outWidth / ww); } else if (w <= h && h > hh) {//如果高度高的话根据宽度固定大小缩放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;//设置缩放比例 //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 bitmap = BitmapFactory.decodeFile(srcPath, newOpts); if(bitmap == null){ return null; } bitmap = BitmapUtil.rotateBitmap(srcPath, bitmap); return bitmap; }
上述代码的