高效地加载Bitmap

概述

现在的图片是动辄几M到几十M,而系统分配给应用的内存有限,如果直接将原图载入内存,这会导致Bitmap加载的时候很容易出现内存溢出(OOM)。

Bitmap高效加载的策略:根据图片展示控件的尺寸,将图片以一定的采样率进行缩放后再加载。这样就能降低内存占用,从而在一定程度上避免OOM,并提高Bitmap加载时的性能。

Bitmap的加载方式

BitmapFactory提供了四类方法来加载Bitmap:

decodeFile:从文件加载Bitmap

decodeResource:从资源中加载Bitmap

decodeStream:从输入流中加载Bitmap

decodeByteArray:从字节数组中加载Bitmap

这四类方法都分别有一个带BitmapFactory.Options参数的重载方法,通过对这个参数的配置从而达到高效加载Bitmap。

BitmapFactory.Options的属性

inSampleSize

inSampleSize:即采样率,通过对其设置,实现图片的宽和高缩放。

当inSampleSize=1:采样后的图片大小为图片的原始大小。

当inSameleSize<1:按=1计算

当inSampleSize>1:采样后的图片将会缩小,缩放比例为1 / (inSampleSize的2次方)。

inSampleSize取值:inSampleSize的取值应该总是2的指数,如1,2,4,8等,如果传入的inSampleSize的值不为2的指数,那么系统会向下取整并选择一个最接近2的指数来代替。比如3,系统会选择2来代替。

示例:

一张2048x1536像素的图片,采用ARGB_8888进行存储,那么内存大小2048 x 1536 x 4 = 12M,如果inSampleSize = 4,那么采样后的图片内存大小:512 x 384 x 4 = 0.75M

inJustDecodeBounds

在计算图片缩放比的时候,我们需要先获取到图片的原始宽高,通过设置inJustDecodeBounds=true,就可以在不将图片加载进内存的情况下,解析出图片的宽高信息。计算出缩放比后,再设置inJustDecodeBounds=false,根据缩放比加载缩放后的图片。

高效加载Bitmap流程

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

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {

// 首次加载获取图片的原始宽高
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);

// 计算缩放比
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// 重新加载图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}

public static int calculateInSampleSize (BitmapFactory.Options options, int reqWidth, int reqHeight) {
// 图片的原始宽高
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;

// 计算缩放比,是2的指数,
// 取宽高的最小缩放比,如宽的缩放比为2,高的缩放比为4,那么取2作为整体的缩放比
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}

return inSampleSize;
}

使用:

1
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

参考链接

  1. 彻底理解Bitmap的高效加载策略
  2. Loading Large Bitmaps Efficiently
文章目录
  1. 1. 概述
  2. 2. Bitmap的加载方式
  3. 3. BitmapFactory.Options的属性
    1. 3.1. inSampleSize
    2. 3.2. inJustDecodeBounds
  4. 4. 高效加载Bitmap流程
  5. 5. 参考链接
,