Android Bitmap 的优化

1. 概述

Android 中的图片是以 Bitmap 方式存在的,绘制的时候也是 Bitmap,直接影响到app运行时的内存。通过 ImageView 来显示图片,很多时候 ImageView 并没有原始图片的尺寸那么大,这个时候把整个图片加载进来后再设置给 ImageView,显然是没有必要的,因为 ImageView 根本没办法显示原始图片。可以将图片缩小后再加载进来,这样图片既能在 ImageView 显示出来,又能降低内存占用从而在一定程度上避免OOM,提高了 Bitmap 加载时的性能。在Android,Bitmap 所占用的内存计算公式是:Bitamp 占用内存大小 = 宽度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一个像素所占的内存只要通过改变宽像素、高像素、一个像素所占的内存这3个参数,任意减少一个的值,就达到了压缩的效果。

2. 单个像素点占用的字节数

单个像素的字节大小由 Bitmap 的一个可配置的参数 Config 来决定,Config 是枚举类,定义了 Android中 支持的 Bitmap 配置:

Config占用字节大小(byte)说明
ALPHA_81每个像素存储为单个半透明(alpha)通道。
RGB_5652简易RGB色调
ARGB_44444已废弃
ARGB_88884默认图片配置
RGBA_F168Android 8.0 新增
HARDWARESpecialAndroid 8.0 新增 (Bitmap直接存储在graphic memory)

这个是理论结论,在实际使用过程中,如果设置成了其他标准。在很多情况下,是不生效的。Android系统会强行转成使用ARGB_8888标准。

如下所示是不同 Config 下,同一张图片的占用的内存大小:

        if (type.equals("alpha_8")) {
            mConfig = Bitmap.Config.ALPHA_8;
        } else if (type.equals("8888")) {
            mConfig = Bitmap.Config.ARGB_8888;
        } else if (type.equals("565")) {
            mConfig = Bitmap.Config.RGB_565;
        } else if (type.equals("f16")) {
            mConfig = Bitmap.Config.RGBA_F16;
        } else if (type.equals("hardware")) {
            mConfig = Bitmap.Config.HARDWARE;
        }
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inPreferredConfig = mConfig;
        try {
            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg, opts);
        } catch (OutOfMemoryError error) {
            mBitmap = null;
        }
        Log.e("zzw", "config:  " + type);
        Log.e("zzw", "width:  " + mBitmap.getWidth() + "  height: " + mBitmap.getHeight());
        Log.e("zzw", "size:  " + mBitmap.getByteCount());
        mImg.setImageBitmap(mBitmap);

log 打印: 

2019-11-07 05:15:16.930 19676-19676/cn.zzw.bitmapdemo E/zzw: config:  alpha_8
2019-11-07 05:15:16.930 19676-19676/cn.zzw.bitmapdemo E/zzw: width:  1313  height: 738
2019-11-07 05:15:16.930 19676-19676/cn.zzw.bitmapdemo E/zzw: size:  3875976
2019-11-07 05:15:27.835 19676-19676/cn.zzw.bitmapdemo E/zzw: config:  8888
2019-11-07 05:15:27.835 19676-19676/cn.zzw.bitmapdemo E/zzw: width:  1313  height: 738
2019-11-07 05:15:27.835 19676-19676/cn.zzw.bitmapdemo E/zzw: size:  3875976
2019-11-07 05:15:34.442 19676-19676/cn.zzw.bitmapdemo E/zzw: config:  565
2019-11-07 05:15:34.442 19676-19676/cn.zzw.bitmapdemo E/zzw: width:  1313  height: 738
2019-11-07 05:15:34.442 19676-19676/cn.zzw.bitmapdemo E/zzw: size:  1937988
2019-11-07 05:15:39.743 19676-19676/cn.zzw.bitmapdemo E/zzw: config:  f16
2019-11-07 05:15:39.744 19676-19676/cn.zzw.bitmapdemo E/zzw: width:  1313  height: 738
2019-11-07 05:15:39.744 19676-19676/cn.zzw.bitmapdemo E/zzw: size:  7751952
2019-11-07 05:15:44.425 19676-19676/cn.zzw.bitmapdemo E/zzw: config:  hardware
2019-11-07 05:15:44.425 19676-19676/cn.zzw.bitmapdemo E/zzw: width:  1313  height: 738
2019-11-07 05:15:44.425 19676-19676/cn.zzw.bitmapdemo E/zzw: size:  3875976

3. Bitmap 压缩

3.1 采样率压缩

采样率压缩其原理其实也是缩放 bitamp 的尺寸,通过调节其 inSampleSize 参数,比如调节为2,宽高会为原来的1/2,内存变回原来的1/4。

            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inSampleSize = 2;
            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg, opts);

 应用场景: 先获取 Bitmap 的宽高,以及要显示的大小,通过比较来决定 inSampleSize 的具体大小。在获取 Bitmap 宽高的时候,设置 inJustDecodeBounds=true,这样创建的 Bitmap 不占用内存的。

3.2 质量压缩

在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的。bitmap 图片的大小不会改变。

        BitmapFactory.Options opts = new BitmapFactory.Options();
        Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.timg, opts);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.JPEG, 50, bos);
        byte[] bytes = bos.toByteArray();
        mBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

 应用场景:用于对图片进行压缩,用于分享图片以及保存为文件用于上传到服务端。

3.3 缩放法压缩

Android 中使用 Matrix 对图像进行缩放、旋转、平移、斜切等变换的。

            Matrix matrix = new Matrix();
            matrix.setScale(0.5f, 0.5f);
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg);
            mBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            bitmap.recycle();
            bitmap = null;

应用场景:跟 3.1 中类似,3.1 中宽高同时进行缩放,这里可以分别对宽高进行缩放。

4. Bitmap 的复用

BitmapFactory.Options.inBitmap 字段,设置此字段之后解码方法会尝试复用一张存在的 Bitmap。Bitmap 的内存被复用,避免了内存的回收及申请过程,性能表现更佳。

            BitmapFactory.Options opts1 = new BitmapFactory.Options();
            opts1.inMutable=true;
            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.na,opts1);
            BitmapFactory.Options opts2 = new BitmapFactory.Options();
            opts2.inBitmap=mBitmap;
            opts2.inSampleSize = 1;
            Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.timg,opts2);
            mImg.setImageBitmap(bitmap2);

用了 inBitmap 这个属性,加载两张,这两张图片会指向同一块内存,而不用开辟两块内存空间。

inBitmap 的限制:

3.0-4.3:复用的图片大小必须相同,编码必须相同;4.4以上:复用的空间大于等于即可,编码不必相同,不支持WebP。

图片复用,这个属性必须设置为true:options.inMutable = true;

5. LruCache 的使用

关于 LruCache 的使用,参考此篇:LruCache 源码解析

 

附上文章中的例子:

https://download.csdn.net/download/zzw0221/11967383

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值