Android的Bitmap图片压缩与内存的关系

摘要:
     我们做Android应用的时候最不可避免的就是与图片打交道,而图片通常又是应用内存开销,影响性能的大头,因而这也是网上的帖子关于图片方面的内容热度比较高的原因之一。另外,发现身边的一些做Android应用开发的朋友,在处理图片这方面的问题的时候,基本就是“一把梭,拿起就干“,从网上拷贝了代码贴上去看到没报错了,就完事。缺乏深度的思考,孔子曰“学而不思则罔” ,这也就促使我冲动的写下该文。

一、图片大小

       了解图片大小之前,先说下图片的存放形式:本地文件、流和Bitmap三种,或许我们会疑问,流和Bitmap不都存放在内存当中吗,那岂不应该是同一种形式才对吗?实际上,都存放进内存,没错,但它们两者在内存中的数据结构是不一样的,即表示每个像素的数据是不相等的。那么三种形式图片的计算是怎样的?
       
       A、本地文件和流都是通过计算文件的长度得到图片的大小,如:
         File f = new File(path);
         long size = f.length();
         所以,本地文件的大小 = 图片流的大小,即本地存放一张1M的图片,通过流的方式读到内存当中,流的大小也是1M。
       B、Bitmap的大小 = bitmap.getRowBytes() * bitmap.getHeight() ,一行的字节数 x 高度。

       经常我们会看到,本地一张1M的图片以Bitmap的方式读到内存中,会比流的方式读到内存中大好几倍。这是因为,本地的图片通常都是通过Bitmap的方式压缩(有损或算法的压缩方式)保存到本地的。如,一张Bitmap.Config.ARGB_8888类型的图片以 Bitmap.Config.RGB_565的类型保存到本地,那么保存后的图片大小就会比Bitmap在内存中的大小小很多,究其原因,就是他们表示每个像素的数据变了,后者没有alpha通道数据,不支持透明和半透明。到此,我觉得有必要让我们来了解一下Bitmap的Config类型: 


上面图是从官网截图下来的,然而,我再用我的理解来详说下,这几种类型的区别:
1、Bitmap.Config.ALPHA_8  表示图片只有alpha值,没有RGB值,1个像素占用一个字节 
2、Bitmap.Config.ARGB_4444 表示一个像素占用2个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占4个bites共16bites,即2个字节 
3、Bitmap.Config.ARGB_8888 表示一个像素占用4个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8个bites,共32bites,即4个字节。(Android上Bitmap的默认格式) 
4、Bitmap.Config.RGB_565 表示一个像素占用2个字节,没有alpha(A)值,即不支持透明和半透明,Red(R)值占5个bites ,Green(G)值占6个bites ,Blue(B)值占5个bites,共16bites,即2个字节。

ok,了解了Bitmap图片不同类型所占据的内存字节数后,对于刚才上面提到的本地图片与Bitmap图片大小差异的问题,应该就迎刃而解了。

二、Bitmap的图片压缩

上面说到了图片的大小,实际上,图片的大小应该分为质量的大小和尺寸的大小。那么,Bitmap图片的压缩也应该包括质量的压缩和尺寸的压缩两种。
       
1、图片质量压缩
public static boolean saveImageFileToLoc(final File file, final Bitmap photoBitmap) {
    FileOutputStream fos = null;
    try {
        if (null != file) {
            fos = new FileOutputStream(file);
            if (null != fos) {
                photoBitmap.compress(Bitmap.CompressFormat.JPEG,75, fos);
                fos.flush();
            }
            return true;
        }
    } catch (IOException e) {
        return false;
    } finally {
        photoBitmap.recycle();
        try {
            if (null != fos) {
                fos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return false;
}
上面是对Bimtap图片进行质量的压缩,如果原来Bitmap的图片类型是 Bitmap.Config.ARGB_8888,那么现在使用Bitmap.CompressFormat.JPEG的方式压缩,每个图片像素存储大小是会变小的,另外,compress这种方法的压缩,实际是一种算法(哈夫曼)的压缩,打个比方:原来用a b c d来存储一个像素,现在经过一定的算法计算后,使用另外一个或多个更小的字节来存储,从而达到压缩的作用,其中75就是计算压缩的程度。这种图片的压缩使用情景是,我们要保存一张图片、或上传图片到服务器的时候,对图片进行了压缩能有效的减少图片在手机当中存储空间,或上传的流量。那么,从这里看,图片最终存在在本地,图片的质量压缩与内存大小实际上是无关的。

2、图片尺寸压缩
public static Bitmap resizeBitmap(String imgPath, float pixelW, float pixelH) {
    BitmapFactory.Options newOpts = new BitmapFactory.Options();
    // 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容
    newOpts.inJustDecodeBounds = true;
    newOpts.inPreferredConfig = Bitmap.Config.ARGB_8888;
    // Get bitmap info, but notice that bitmap is null now
    Bitmap bitmap = BitmapFactory.decodeFile(imgPath,newOpts);

    newOpts.inJustDecodeBounds = false;
    int w = newOpts.outWidth;
    int h = newOpts.outHeight;
    // 想要缩放的目标尺寸
    float hh = pixelH;// 设置高度为240f时,可以明显看到图片缩小了
    float ww = pixelW;// 设置宽度为120f,可以明显看到图片缩小了
    // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
    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(imgPath, newOpts);
    return bitmap ;
}
上面的压缩是根据指定的图片尺寸进行采样率的压缩,是把本地图片转到Bitmap,也即图片从本地到内存,同时本地的图片转Bitmap的过程中,因为不同的Bitmap类型,所占的内存就会有很大的差异,另外,不同的采样率会影响像素的个数,在每个像素占据内存大小一定的情况下,像素的个数直接决定了Bitmap图片占据内存的大小。所以,在使用场景当中,经常我们会出现OOM的问题,这个时候,我们就可以通过改变Bitmap的类型和采样率来降低内存的大小,从而避免出现图片的OOM问题。
    
 总结:本文只是通过对图片大小的介绍以及两种压缩方式的对比,来进一步说明我们使用图片压缩的过程中,对内存所产生的 影响,以及这两种Bitmap图片压缩方式的正确使用场景。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值