美文网首页
Android图片加载解析之Bitmap

Android图片加载解析之Bitmap

作者: 顶级工程师闯天涯 | 来源:发表于2017-07-08 20:47 被阅读544次

    写在前面的话,本篇文章是参考自《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();

    看图更明白一些:

    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这个变量;

    采样率.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

          本文链接:https://www.haomeiwen.com/subject/kgothxtx.html