Android图片加载解析之Bitmap

写在前面的话,本篇文章是参考自《Android开发艺术探索》所写,看此书已是2015年的事情啦,由于独立开放项目,以至于对于Android原理性东西生疏,最近需要换工作,重新捡起此书,仍有大的收获。故在此留下一笔。(总结是很有必要的)

在Android图片加载方面,我们少不了与Bitmap(位图)打交道,但是与它相处需要步步谨慎啊,稍不留神就跟OOM(内存溢出)见面啦。
> java.lang.OutofMemoryError:bitmap size exceeds VM budget
如何高效地加载Bitmap是我们每一个开发者都不容忽视的问题。
下面我们来逐步地深入探讨这个问题:

一:如何加载Bitmap

Bitmap在Android中简单理解为一张图片,图片的格式可以是(JPEG ,PNG ,WEBP等)。

BitmapFactory为我们提供了4类方法来加载Bitmap:
1.decodeFile();
2.decodeResource();
3.decodeStream();
4.decodeByteArray();

看图更明白一些:

Android图片加载解析之Bitmap_第1张图片
Bitmap加载方式.png

如果深入去查看这些方法,会发现其中** decodeFile()和decodeResource()都间接调用了decodeStream() 。这四类加载方法最终都是在Android低层实现的,对应着BitmapFactory类的几个native方法。
到了这里,Bitmap的简单加载就介绍完毕,但是Bitmap的加载远没有结束,因为如果我们这么简单使用,会经常与
OOM**邂逅。这就需要进行高效设置啦。

二:如何高效地加载Bitmap

首先来交代一下背景:其实所谓高效,只不过就是不浪费系统那宝贵的资源。在Android开发中,与Bitmap打交道最多的控件要属ImageView。在很多情况下,ImageView都没有图片的原始尺寸那么大,这时如果我们不加任何设置而直接让ImageView来显示图片。Android系统往往都是先将整个图片加载到内存,然后再显示出来。这显然是画蛇添足。

我们可以通过BitmapFactory.Options来通过设置采样率来加载所需尺寸的图片,这样就降低内存占用从而在一定程度上降低了OOM的发生率。BitmapFactory提供的加载图片的四类方法都支持BitmapFactory.Options参数,通过它我们就可以很方便地进行采样缩放。(意不意外,惊不惊喜?)
那么我们是如何通过BitmapFactory.Options来进行采样缩放的呢?
这里主要用到了inSampleSize这个变量;

Android图片加载解析之Bitmap_第2张图片
采样率.png

从图上的注释我们可以知道:

  • 如果inSampleSize > 1,将会给我们返回一个比原图更小的图片来节约内存。举个例子,当设置 inSampleSize = 4时,那么采样后的图片其宽和高都缩小为原图的1/4,而像素数为原图的1/16,这样其所占用的内存也只有原来的1/16。
  • 当 inSampleSize <= 1时,系统都会当成1来对待;
  • 这个inSampleSize 的取值应该总为2 的指数(1,2,4,8,18...),如果外界传递给系统的inSampleSize不为2的指数,则系统会向下取整并选择一个最接近2的指数来代替。

获取采样率的流程:

  1. 获取BitmapFactory.Options的对象;
  1. 将BitmapFactory.Options的inJustDecodeBounds参数设置为true并加载图片;
  2. 从BitmapFactory.Options中取出原始图片的宽高信息,即outWidth和outHeight参数。
  3. 根据采样率的规则并结合目标View的所需大小计算采样率inSampleSize。
  4. 将BitmapFactory.Options的inJustDecodeBounds参数设置为false,然后重新加载图片。

针对于inJustDecodeBounds这个变量:

将 inJustDecodeBounds参数设置为ture时,BitmapFactory只会解析图片的原始宽高信息,并不会真正地加载图片,所以这个操作是轻量级的。

    "Talk is cheap. Show me the code."           - Linus Torvalds
  // 从res中加载bitmap
 public static Bitmap decodeBitmapFromRes(Resources res,int resId,
                                             int requestWidth,
                                             int requestHeight){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res,resId,options);
      
        //设置采样率
        options.inSampleSize = calculateInSampleSize(options,requestWidth,requestHeight);
        
        options.inJustDecodeBounds = false;
        
        return BitmapFactory.decodeResource(res,resId,options);
    }

//计算采样率
 private static int calculateInSampleSize(BitmapFactory.Options options, 
                                             int requestWidth, 
                                             int requestHeight) {
        int outWidth = options.outWidth;
        int outHeight = options.outHeight;

        int inSampleSize = 1;

        if(outHeight>requestHeight || outWidth > requestWidth){
            int halfHeight = outHeight / 2;
            int halfWidth = outWidth / 2;

            while ((halfHeight/ inSampleSize) >= requestHeight
                    && (halfWidth / inSampleSize) >= requestWidth){
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

到此,Bitmap加载可以在实际开发工作中使用啦。

你可能感兴趣的:(Android图片加载解析之Bitmap)