写在前面的话,本篇文章是参考自《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这个变量;
从图上的注释我们可以知道:
- 如果inSampleSize > 1,将会给我们返回一个比原图更小的图片来节约内存。举个例子,当设置 inSampleSize = 4时,那么采样后的图片其宽和高都缩小为原图的1/4,而像素数为原图的1/16,这样其所占用的内存也只有原来的1/16。
- 当 inSampleSize <= 1时,系统都会当成1来对待;
- 这个inSampleSize 的取值应该总为2 的指数(1,2,4,8,18...),如果外界传递给系统的inSampleSize不为2的指数,则系统会向下取整并选择一个最接近2的指数来代替。
获取采样率的流程:
- 获取BitmapFactory.Options的对象;
- 将BitmapFactory.Options的inJustDecodeBounds参数设置为true并加载图片;
- 从BitmapFactory.Options中取出原始图片的宽高信息,即outWidth和outHeight参数。
- 根据采样率的规则并结合目标View的所需大小计算采样率inSampleSize。
- 将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加载可以在实际开发工作中使用啦。
网友评论