Android中图片的压缩

Android中的图片一般在内存中显示是以位图的方式实现的,在磁盘上主要以扩展名为.jpeg,.png,.webp格式的文件存储。
图片占用的空间分为内存空间和磁盘空间。内存空间即从磁盘或网络读取图片加载到内存中所占用的内存字节数大小。磁盘空间即一张存放在手机存储介质里的一张图片占用存储介质的字节数。
内存空间和磁盘空间是两个完全不同的概念,但是很多初学者甚至工作3年以上的都没有搞清楚这件事情,很是尴尬。例如(仅仅是举例)一张1280*720编码方式是ARGB8888的17M的图片,总有人认为把图片压缩到2M大小(仅仅是质量压缩,减少了磁盘占用大小)然后显示就可以在类似ListView这样的列表直接显示了。或者将这样一张图片显示在一个大小比较小的Imagevivw控件上,感觉这是一张小图。这样简单粗暴的显示是非常危险的,很容易内存溢出(OOM)具体原因将在下面的小节中分析。
为了解决图片加载容易导致内存溢出、网络传输耗时问题,所以我们需要对图片的内存占用大小、磁盘占用大小进行压缩优化。接下来分别介绍图片的质量质量压缩和尺寸压缩。

JPEG、PNG、WEBP三种质量压缩的方法

图片的质量压缩不影响图片的尺寸、像素。只能影响文件占用磁盘的大小、网络传输的字节流大小。所以可以利用图片的质量压缩减少图片文件的磁盘占用以及传输字节流的大小,提高图片上传下载的用户体验。接下来介绍三种质量压缩的方法特点:
1、JPEG压缩是一种有损压缩并且支持最高级别的压缩。该方式的压缩可以有效的减少磁盘空间和网络传输过程的流量消耗。如下所示所示是对图片进行JPEG压缩的关键代码:

BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(mFile));
mBitmap.compress(Bitmap.CompressFormat.JPEG, 50, bos);
bos.flush();
bos.close();

说明:Bitmap.CompressFormat.JPEG代表的是JPEG压缩,mFile 是压缩后的图片文件,50是质量压缩比,范围值在0-100之间,数值越小代表压缩的也就越厉害。
2、PNG压缩是一种无损耗的压缩,并且支持透明度。该方式不能减少磁盘空间的占用和网络传输过程的流量消耗。PNG是无损压缩,所以质量压缩可以忽略。

3、Webp是一种有损压缩,Google开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器带宽资源和数据空间。如下所示所示是对图片进行WEBP压缩的关键代码:

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(mFile));
mBitmap.compress(Bitmap.CompressFormat.WEBP, 50, bos);
bos.flush();
bos.close();

说明:Bitmap.CompressFormat.WEBP代表的是WEBP压缩。mFile 是压缩后的图片文件,50是质量压缩比,范围值在0-100之间,数值越小代表压缩的也就越厉害。

质量压缩在一定程度上解决了图片占用磁盘空间较大,网络传输字节流较大的问题,但是图片的质量压缩并没有改变图片的尺寸,导致磁盘占用和网络传输字节流减少的图片占用的内存一直没有变化。所以图片的显示成为吃内存大户,大量图片的显示,类似微信消息列表的九宫格头像显示的业务场景就特别容易内存溢出。所以为了解决大量图片的加载显示内存溢出问题需要对图片的尺寸做压缩,下节将说明图片尺寸压缩的方式。

图片内存压缩方式

图片的内存压缩方式即图片的尺寸压缩。图片尺寸压缩的关键是计算出采样率,通过计算出的采样率进行图片的尺寸压缩。计算采样率步骤如下所示:
1、获取原始图片的宽高,关键代码如下所示:

Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getPath(), options);
int height = options.outHeight;
int width = options.outWidth;
options.inJustDecodeBounds = false;

说明:通过BitmapFactory.decodeFile(file.getPath(), options)方法将本地文件和options参数进行关联。然后通过Options 类参数获取到本地的待压缩的图片的宽高width、height。对于参数inJustDecodeBounds ,只会去解析原始图片的宽高,并没有去加载图片属于轻量级操作。参数inJustDecodeBounds=true和inJustDecodeBounds =false需要配对使用。

2、与期望压缩的尺寸进行对比计算出最佳的采样率

/**
 * 计算图片的缩放值
 * @param options
 * @param reqWidth 
 * @param reqHeight
 * @return
 */
private int calculateInSampleSize(Options options,int reqWidth, int reqHeight) {
   try {
      int height = options.outHeight;
      int width = options.outWidth;
      int inSampleSize = 1;
      if (height > reqHeight || width > reqWidth) {
         int heightRatio = Math.round((float) height/ (float) reqHeight);
         int widthRatio = Math.round((float) width / (float) reqWidth);
         inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
      }
      return inSampleSize;
   } catch (Exception e) {
      Log.e(TAG, e.toString());
      return 1;
   }
}

说明:options是第一步计算出的Options的对象。reqWidth、reqHeight是期望压缩的尺寸。
通过以上两步骤即可计算出最佳的采样率,然后将上面计算出的采样率赋值给options变量即可。关键代码如下所示:

options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
Bitmap bitmap= BitmapFactory.decodeFile(file.getPath(), options);

说明:获取到采样率后即可通过BitmapFactory的方法传入参数生成指定尺寸的位图了。
到这里,我们将原始的图片压缩成我们需要的尺寸了。这样图片加载到内存中的内存占用确实可以减少很多了。既然已经拿到了BitMap对象就可以好好的利用该对象的相关方法对位图参数做进一步的配置。在这之前我们要说明下图片在内存中显示的时候内存是怎样计算的。一张位图所占内存如下所示:

位图加载所占内存=图片的宽图片的高像素点占用的字节数

图片的像素点所占的字节数怎么计算呢?首先需要了解图片的四个通道ARGB即图片的透明度、红色、绿色、蓝色。位图的像素主要有以下配置:
这里写图片描述

我们项目中比较常用的即RGB_565(2个字节),ARGB_8888(4个字节)。所以对于开篇举例的图片内存占用将是1280*720*4字节大小。一张压缩了的图片居然占用了那么大的内存空间,是不是好惊喜好意外。所以需要进一步对像素点的存储按需优化。通过Bitmap的setConfig方法对对像素点存储进行优化,关键代码如下所示:

bitmap.setConfig(Bitmap.Config.ARGB_8888);
bitmap.setConfig(Bitmap.Config.RGB_565);

说明:RGB_565适合做缩略图的的像素点存储,ARGB_8888适合做详情图片的查看的像素点存储。其中RGB_565图片内存占用是ARGB_8888的一半。

总结:本文说明了图片压缩的方式,主要是图片的质量压缩和尺寸压缩。质量压缩可以减少图片的磁盘占用和网络传输字节流,提升图片上传下载的用户体验。图片的尺寸压缩,主要是通过获取原始图片的尺寸与目标压缩尺寸对比计算出最佳采样率,从而得到尺寸压缩到指定大小的位图,最后通过指定位图的像素点存储方式进一步减少图片的内存占用。

附:图片的缩略图生成工具类

ThumbnailUtils.extractThumbnail(mBitmap,reqwidth,reqHeight);  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值