Android Bitmap 的高效加载

379 阅读2分钟

首先看代码

 public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){
        // 1、将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res,resId,options);
        options.inSampleSize = calculateInSampleSize(options,reqHeight,reqWidth);
        // 4、将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片
        options.inJustDecodeBounds =false;
        return BitmapFactory.decodeResource(res,resId,options);
    }

    private static int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) {
        // 2、从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数
        int height = options.outHeight;
        int width = options.outWidth;
        // 3、根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize
        int inSampleSize = 1;
        if(height>reqHeight||width>reqWidth){
            int halfHeight = height/2;
            int halfWidth = width/2;
            //计算缩放比,是2的指数
            while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
                inSampleSize*=2;
            }
        }
        
        
        return inSampleSize;
    }

通过如下方式可以加载图片

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.mipmap.ic_launcher,100,100);

知识点

1. 加载 Bitmap 的方法
BitmapFactiory 类提供了四中方法:decodeFile、decodeResource、decodeStream和decodeByteArray,分别用于支持从文件系统、资源、输入流以及字节数组中加载出一个 Bitmap 对象,其中decodeFile和decodeResou又间接调用了decodeStream方法,这四类方法最终在Android的底层实现,对应这BitmapFactory类的几个native方法。

2. BitmapFactory.Options 的参数

  1. 当 inSampleSize 为1时,采样后的图片大小为图片的原始大小;
  2. 当 inSampleSize 大于1时,比如为2,那么采用后的图片宽高为原图片大小的1/2,而像素为原图的1/4,其占有的内存大小也为原图的1/4;
  3. 当 inSampleSize 小于1时,其作用相当于1,即无缩放效果;
    注意官方文档指出,inSampleSize的取值应该总为2的指数,比如 1、2、4、8、16等。如果不为2的指数,那么系统回向下取整并选择一个最接近2的指数来代替,比如3,系统回选择2代替。

我们需要获取加载的图片的宽高信息,然后交给inSampleSize参数选择缩放比缩放。那么如何能先不加载图片却能获得图片的宽高信息?

  1. 通过inJustDecodeBounds=true,然后加载图片就可以实现只解析图片的宽高信息,并不会真正的加载图片,所以这个操作是轻量级的。
  2. 获取了宽高信息,计算出缩放比后,然后在将inJustDecodeBounds=false,再重新加载图片,就可以加载缩放后的图片。
    注意:BitmapFactory获取的图片宽高信息和图片的位置以及程序运行的设备有关,比如同一张图片放在不同的drawable目录下或者程序运行在不同屏幕密度的设备上,都可能导致BitmapFactory获取到不同的结果,和Android的资源加载机制有关。

参考:《Android 开发艺术探索》