您的位置:首页 > 移动开发 > Android开发

Android开发 —— Bitmap的高效加载

2017-03-27 14:13 302 查看
0. 前言

图片加载是Android开发的重要环节之一。现在,第三方图片加载框架很流行,如bumptech的Glide、Square的Picasso、Facebook的Fresco以及Google的网络请求框架Volley。

其中,Glide是Google官方推荐的图片加载框架。当然,这些主流的框架都包含了图片的高效加载策略和缓存策略。

0.1 为什么需要Bitmap的高效加载

Bitmap在Android中指的是一张图片,可以是png格式也可以是jpg等其他常见格式。

现在的图片都比较大,而Android对单个应用的内存占用有所限制,这就会导致在加载Bitmap的时候容易出现内存溢出OOM。

为了解决这个问题,就出现了Bitmap的高效加载策略。

它的核心思想很简单,就是采用BitmapFactory.Options来加载所需尺寸的图片。

假设通过ImageView来显示图片,很多时候ImageView并没有原始图片的尺寸那么大,这个时候把整个图片加载进来后再设置给ImageView,

显然是没有必要的,因为ImageView根本没办法显示原始图片。这时候就可以按一定的采样率来将图片缩小后再加载进来,

这样图片既能在ImageView显示出来,又能降低内存占用从而在一定程度上避免OOM,提高了Bitmap加载时的性能。

1. 加载Bitmap

BitmapFactory类提供了4个方法:

decodeFile()、decodeResource()、decodeStream()和decodeByteArray()

分别用于从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象。

2. 高效加载Bitmap

通过BitmapFactory.Options可以按一定的采样率来加载缩小后的图片。

BitmapFactory提供的加载图片的4类方法都支持BitmapFactory.Options参数。

2.1 inSampleSize参数

即采样率

当inSampleSize小于等于1时,无缩放效果,即采样后的图片大小为图片的原始大小;

当inSampleSize大于1时,采样后的图片大小为原图片大小的1 / (inSampleSize的平方)。

inSampleSize的取值应为2的指数,如1、2、4、8、16等。

例子:一张1024*1024像素的图片,假定采用ARGB8888存储,那么它占内存为1024*1024*4=4MB。

如果inSampleSize=2,那么采样后的图片占内存为512*512*4=1MB。

inSampleSize参数的取值应根据ImageView的大小和图片的原始大小来选择。

2.2 inJustDecodeBounds参数

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

注:此时,BitmapFactory获取的图片宽高信息和图片位置与程序运行的设备有关。

比如同一张图片放在不同的drawable目录下或者程序运行在不同屏幕密度的设备上,都可能导致BitmapFactory获取不同的结果。

之所以会出现这个现象,这和Android的资源加载机制有关。

3. 高效加载Bitmap的流程

(1) 将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。

(2) 从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数。

(3) 根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。

(4) 将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。

经过上面的4个步骤,加载出来的图片就是最终缩放后的图片,当然也有可能不需要缩放。

4. 高效加载Bitmap的代码实现

public static Bitmap decodeSampledBitmapFromResource(Resource res, int resId,
int reqWidth, int reqHeight) {
// 将inJustDecodeBounds参数设为true并加载图片
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);

// 计算采样率inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// 将inJustDecodeBounds参数设为false并重新加载图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}

public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqWidth) {
// 取出图片的原始宽高信息
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;

if (width > reqWidth || height > reqHeight) {
final int halfWidth = width / 2;
final int halfHeight = height / 2;
// 根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize
while ((halfWidth / inSampleSize) >= reqWidth
&& (halfHeight / inSampleSize) >= reqHeight) {
inSampleSize *= 2;
}
}

return inSampleSize;
}
实际使用:比如ImageView所期望的图片大小为100*100像素,可以通过以下方式高效加载并显示图片

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResource(), R.id.myimage, 100, 100));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: