Android图片压缩浅谈

前些天参加面试时,被问到的一个问题是:Android里面压缩图片有什么方法。当时只用了瀑布流照片墙的一些方法来回答,还是不够清楚,答得不太满意。现在就来总结一下Android中压缩图片的一些基本知识和流程。

一、引言

在移动开发中,图片压缩是一个非常重要的方面,因为设备内存有限,如果不对图片进行处理的话,可能会引起OOM;在瀑布流等图片经典demo中也使用了相应的处理,压缩图片等等。

现在先来了解一下Android图片的存在方式:
1.文件形式(即以二进制形式存在于硬盘上)
2.流的形式(即以二进制形式存在于内存中)
3.Bitmap形式
这三种形式的区别: 文件形式和流的形式对图片体积大小并没有影响,也就是说,如果你手机SD卡上的如果是100K,那么通过流的形式读到内存中,也一定是占100K的内存,注意是流的形式,不是Bitmap的形式,当图片以Bitmap的形式存在时,其占用的内存会瞬间变大, 增大的倍数是很可观的

检测图片三种形式大小的方法:
文件形式: file.length()
流的形式: 讲图片文件读到内存输入流中,看它的byte数
Bitmap: bitmap.getByteCount()

二、常用的图片压缩方法

**

1. BitmapFactory的decodeFile方法

**

特点: 通过设置采样率, 减少图片的像素, 达到对内存中的Bitmap进行压缩

首先,在处理图片的方法中,官方demo中推荐的也是这种方法。
结合现实场景来看,我们通常会将图片固定大小成缩略图,使用的就是decodeFile方法,然后通过传递进去 BitmapFactory.Option类型的参数取缩略图。

Option类在百度地图sdk或者其它地方中也看到过,可以说是参数或者限定的集和吧。在Option中,有两个重要的地方,一是inSampleSize表示采样率长宽压缩的比例,另外的就是inJustDecodeBounds,就是表示只包括一些解码边界信息即图片大小信息等等。举个具体的例子来说,还是以官方demo为主来讲吧:
inSampleSize表示缩略图大小为原始图片大小的几分之一,即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。
而inJustDecodeBounds设置为true的时候,就是提取图片大小信息,不返回实际的bitmap也不给其分配内存空间,但记得用完后就要把它设为false;

/**
* 计算压缩比率
*/
    public static int calculateInSampleSize(BitmapFactory.Options options,
                                            int reqWidth) {
        // 源图片的宽度
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (width > reqWidth) {
            // 计算出实际宽度和目标宽度的比率
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = widthRatio;
        }
        return inSampleSize;
    }

 /**
     * 官方文档的用法改写,这是一个系列的
     * http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
     * @param pathName
     * @param reqWidth
     * @return
     */

    public static Bitmap decodeSampledBitmapFromResource(String pathName,
                                                         int reqWidth) {
        // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(pathName, options);
        // 调用上面定义的方法计算inSampleSize值
        options.inSampleSize = calculateInSampleSize(options, reqWidth);
        // 使用获取到的inSampleSize值再次解析图片
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(pathName, options);
    }

简要总结:
该方法就是对Bitmap形式的图片进行压缩, 也就是通过设置采样率, 减少Bitmap的像素, 从而减少了它所占用的内存。一般过程就是:
使用2的次幂来设置inSampleSize值可以使解码器执行地更加迅速、更加高效。但是,如果你想在内存或者硬盘上缓存一个调整过大小的图片,通常还是解码到合适的图片尺寸更加节省空间。

要使用这个方法,首先要使用inJustDecodeBounds为true来解码尺寸信息,将options传递过去使用新的inSampleSize值再次解码并且要将inJustDecodeBounds值设置为false。最后通过BitmapFactory.decode**的方法压缩。

2. 将图片保存到本地时进行压缩, 即将图片从Bitmap形式变为File形式时进行压缩,

特点是: File形式的图片确实被压缩了, 只对File格式进行质量压缩。注意:如果读取压缩后的file为Bitmap时,它占用的内存并没有改变

public static void compressBmpToFile(Bitmap bmp,File file){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int options = 80;//设置开始的compress系数
        bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
        while (baos.toByteArray().length / 1024 > 100) { 
            baos.reset();
            options -= 10;
            bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
        }
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(baos.toByteArray());
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

方法说明: 该方法是压缩图片的质量, 注意它不会减少图片的像素,比方说, 你的图片是300K的, 1280*700像素的, 经过该方法压缩后, File形式的图片是在100K以下, 以方便上传服务器, 但是你BitmapFactory.decodeFile到内存中,变成Bitmap时,它的像素仍然是1280*700, 计算图片像素的方法是 bitmap.getWidth()和bitmap.getHeight(), 图片是由像素组成的, 每个像素又包含什么呢? 熟悉PS的人知道, 图片是有色相,明度和饱和度构成的.

该方法的官方文档也解释说, 它会让图片重新构造, 但是有可能图像的位深(即色深)和每个像素的透明度会变化,JPEG only supports opaque(不透明), 也就是说以jpeg格式压缩后, 原来图片中透明的元素将消失.所以这种格式很可能造成失真

既然它是改变了图片的显示质量, 达到了对File形式的图片进行压缩, 图片的像素没有改变的话, 那重新读取经过压缩的file为Bitmap时, 它占用的内存并不会少.

所以呢,这种方法比较适用上传服务器时的图片压缩

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值