Android Bitmap优化

开发中经常需要使用Bitmap进行位图显示,由于现在手机像素的提升,直接频繁显示原图会消耗大量的内存,很容易造成OOM,因此我们需要对Bitmap进行压缩处理。

首先在通过BitmapFactory创建Bitmap时可以发现,官方为我们提供了一个option参数,大多数开发者都知道这个参数可以帮助我们调节Bitmap的质量,从而实现图片的压缩。

public enum Config {
        ALPHA_8     (1),
        RGB_565     (3),
        ARGB_4444   (4),
        ARGB_8888   (5);
    }
官方为我们提供了如上几种配置信息。

其实这都是色彩的存储方法:我们知道ARGB指的是一种色彩模式,里面A代表Alpha,R表示red,G表示green,B表示blue,其实所有的可见色都是右红绿蓝组成的,所以红绿蓝又称为三原色,每个原色都存储着所表示颜色的信息值。

说白了其实就是:

ALPHA_8就是Alpha由8位组成,代表8位Alpha位图

ARGB_4444就是由4个4位组成即16位,代表16位ARGB位图

ARGB_8888就是由4个8位组成即32位,代表32位ARGB位图

RGB_565就是R为5位,G为6位,B为5位共16位,代表16位RGB位图

根据上面的参数可以看出,当我们需要对一个图片进行压缩时,如果需要其拥有透明度则选择ARGB_4444,如果不需要透明度则选择RGB_565。虽然这会损失一些细节但是普通显示上是可以接受的。

除了颜色配置,还有一个参数影响着图片大小,那就是分辨率。这里就不多说原因,相信都知道,那就直接说怎么解决。

可以直接通过修改Bitmap大小实现:

/**
     * @param bitmap 源文件
     * @param convertWidth 输出宽度
     * @param convertHeight 输出高度
     * @param option 如果option定义为 OPTIONS_RECYCLE_INPUT,则会回收bitmap源文件,不用手动释放
     */
    public static Bitmap convertBitmap(Bitmap bitmap, int convertWidth, int convertHeight,int option) {
        Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, convertWidth, convertHeight, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
        return thumbnail;
    }
通过Matrix对图片按倍数放大和缩小:

/**
     * 讲bitmap放大或缩小制定的倍数
     * @param bytes bitmap的字节数组
     * @param scale 倍数
     */
    public static byte[] scaleBitmap(byte[] bytes, float scale) {
        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale);
        Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        newBitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
        byte[] byteArray = bos.toByteArray();
        try {
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        bitmap.recycle();
        newBitmap.recycle();
        return byteArray;
    }
上面两种都有一定的局限性,第一个需要指定精确的长宽值,如果指定比例和原始比例不符,会导致图片变形。第二个虽然不会变形,但是只能按倍数增大和缩小。并且这两种都需要先生成一个Bitmap,再通过这个Bitmap进行转换。


最后一种,在Bitmap创建时便直接创建成想要的尺寸:

public interface CompressBitmapCallback {
        void onComplete(Bitmap bitmap);
    }

    private static Executor executor = Executors.newFixedThreadPool(3);

    /**
     * 生成压缩过的bitmap
     *
     * @param height   定制最大高度
     * @param width    定制最大宽度
     * @param path     图片本地路径
     * @param callback 操作完成回调
     */
    public static void getCompressBitmap(final int height, final int width, final String path, final Handler handler, final CompressBitmapCallback callback) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                BitmapFactory.Options options = new BitmapFactory.Options();
                //inJustDecodeBounds为true时创建的Bitmap只有尺寸信息,而没有真正的创建,可以节省资源
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeFile(path, options);
                int bitHeight = options.outHeight;
                int bitWidth = options.outWidth;
                int sample = 1;
                //通过原始尺寸和需要输出尺寸判断sample值
                if (bitHeight > height || bitWidth > width) {
                    while (bitHeight / (2 * sample) >= height || bitWidth / (2 * sample) >= width) {
                        sample *= 2;
                    }
                }
                Bitmap compressBitmap = null;
                options.inJustDecodeBounds = false;
                options.inSampleSize = sample;
                options.inPreferredConfig = Bitmap.Config.RGB_565;
                compressBitmap = BitmapFactory.decodeFile(path, options);
                final Bitmap finalCompressBitmap = compressBitmap;
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        callback.onComplete(finalCompressBitmap);
                    }
                });
            }
        });
    }
这种转换是比较推荐的一种方式,先将options的inJustDecodeBounds属性设为true,得到将要生成Bitmap的尺寸(注意这里其实系统并没有真正生成bitmap,只是拿到了对应的尺寸信息,因此不会消耗太多资源)。然后再通过需要输出的大小进行计算inSampleSize的值,这里解释一下,这个值的大小代表了生成图片会是原图片大小的N分之一,如果是2则为原图的一半,这也是官方比较推荐的一种方式,并且这样生成的图片因为是按比例缩放,所以不会出现图片变形的情况。还有需要注意的是,这个值只有当为2的指数时才会生效,即1、2、4、8。具体原因不详。

采用这种方法可以使用大多数情况,但是同样也有缺点,那就是不能保证输出图片尺寸的精确性,因为缩放比例只能是2的指数,所以和想要的尺寸肯定会存在出入。但是这种方法相对来说最节省资源,并且可以尽量保证图片的显示效果。

通过BitmapFactory.Options来缩放图片,主要用到inSampleSize属性,获得的Bitmap分辨率大小将会为原图的1/inSampleSize,其中该值必须是2的指数,即:1,2,4,8,16等,不满足时绘选择一个最最接近的指数来代替。

为了保证不同分辨率图片处理之后得到同样的效果,现将Options的inJustDecodeBounds设为true,这样生成的bitmap只有尺寸属性而没有真正的图片,通过得到的尺寸来计算inSampleSize的大小,再次生成bitmap即为我们想要得到的最终Bitmap。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值