安卓漫漫路之BitmapFactory高效加载Bitmap.

时间:2022-03-23 01:30:51

BitmapFactory之高效加载Bitmap图片.

------引语:--------
Bitmap简介: Bitmap(位图,位映像) : 在Android中指的是一张图片 . 例如png格式或者jpg格式.
那么如何加载Bitmap呢? 咱们可以利用BitmapFactory类(package android.graphics) , 此类提供了几类加载图片的方法:
  • decodeByteArray(byte[] data, int offset,int length):从指定字节数组的offset位置开始,将长度为length的字节数据解析成Bitmap对象.
  • decodeFIle(String pathName):从pathName指定的文件中解析 , 创建Bitmap对象.
  • decodeFileDescriptor(FileDescriptor fd):用于从FileDescriptor对应的文件中解析 , 创建Bitmap对象.
  • decodeResource(Resource res,int id):用于根据给定的资源ID从指定的资源文件中解析 , 创建Bitmap对象.
  • decodeStream(InputStream is):用于从指定输入流中介解析 , 创建Bitmap对象.
  • 分别用于支持从文件系统 , 资源 , 输入流和字节数组中加载出一个Bitmap对象 . 并且decodeFile函数和decodeResoure函数最终也会调用decodeStream函数.
    PS:此时想到了个题外话 解析字符串时的getString函数和optString函数的区别: 如果字符串为null,前者会报错,后者不会报错. ------正题:--------
    絮絮叨叨半天 , 到底怎么才能高效的加载一个Bitmap对象呢 (个人认为高效加载即为缩放): 核心思想 : 就是采用BitmapFactory.Options类(内部类)来加载所需尺寸的图片 .  此类用于解码Bitmap时的各种参数控制; 加载图片时ImageView是没有原始图片那么大的 . 此时把整个图片完全的加载进ImageView的话显然没有必要 , 并且在Android手机上ImageView也并没有办法显示原图大小,没办法的话咱们就得缩小加载,要问怎么压缩您接着往下看.
    BitmapFactory.Options这个类中: 其中有一个字段叫做 inJustDecodeBounds . SDK中对这个成员的说明是这样的:
    If set to true, the decoder will return null (no bitmap), but the out…
    也就是说,如果我们把它设为true , 那么BitmapFactory.decodeFile(String path, Options opt)并不会真的返回一个Bitmap给你 , 它仅仅会把它的宽和高取回来给你 , 这样就不会占用太多的内存 , 并且我们也需要适时的回收对象 , 利用下方代码:
    if(!bitmap.isRecycled()){  
    bitmap.recycle(); //回收图片所占的内存
    System.gc(); //提醒系统及时回收
    }
    及时回收,就不会那么频繁的发生OOM了,也提高了Bitmap加载时的性能.

    ------开始--------
    得到了原始宽高 , 又有什么用呢 ? 咱们需要的可是缩放啊: 您以为BitmapFactory.Options(内部类)就只能获取原始大小么 ? 那肯定是不可能的 .  BitmapFactory.Options类(内部类)档案也可以用来缩放图片: 主要是用到了他的inSampleSize参数,即采样率.
    inSampleSize简介: 当inSampleSize等于1时,得到的是原图大小; 当inSampleSize等于1时,效果同1,即得到的是原图大小; 当inSampleSize等于2时,那么采样之后的图片宽高均为原图的1/2,而像素数为原图的1/4,其占有的内存大小也为原图的1/4,例如一张1024*1024*4的图片,即4MB,如果inSampleSize为2,那么采样后的图片占内存512*512*4,即1MB. 官方文档指出:inSampleSize的取值规范应该是2的指数,即1,2,4,8,16等.当然,不遵从也说明咱们随性.
    /**   *如何获取采样率   */
    通过采样率可有效的加载图片,那么到底如何获取采样率呢?您可遵循如下流程: (1).将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片. (2).从BitmapFactory.Options中取出原图片的原始宽高信息,他们对应于outWidth参数和outHight参数 (3).根据采样率的规则并结合目标ImageView的所需大小计算出采样率inSampleSize. (4).将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片. 此时加载的图片即为缩放后的图片. 这里说一下inJustDecodeBounds参数,当此参数为true时,BitmapFactory只会解析原图片的原始宽高.并不会去真正的加载图片.如下是抽取的一个工具类
    public class BitMapUtils {
    //高效加载BitMap
    public static Bitmap decodeSampleBitmapFormResource(Resources resources, int resourcesId, int reqWidth, int reqHeight){
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(resources,resourcesId,options);
    options.inSampleSize = caculateInSampleSize(options,resourcesId,reqHeight,reqWidth);
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(resources,resourcesId,options);
    }

    private static int caculateInSampleSize(BitmapFactory.Options options, int resourcesId, int reqHeight,int reqWidth) {
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width >reqWidth){
    final int halfHeight = height / 2;
    final int halfWidth = width / 2;
    while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth){
    inSampleSize *= 2;
    }
    }
    return inSampleSize;
    }
    }




    有了上面两个函数,实际使用的时候就很简单了,比如ImageView所期望的图片大小为100*100像素,那么就可以通过如下方式高效的加载并显示图片:
    mImageView.setImageBitmap(BitMapUtils.decodeSampleBitmapFormResource(this.getResources(),R.drawable.icon,100,100));




    除了BitmapFactory的decodeResource函数,其他三个函数(decodeFile ,  decodeStream和decodeByteArray.)也是支持采样加载的,并且处理方式也是类似的. 

    如有问题请多指正,您的指正使我更我正确的前行.