1.为什么要进行图片的二次采样?
主要是避免OOM。假设客户端需要加载一张图片,图片尺寸为3000*3000(单位/像素),那么计算一下,如果客户端想显示原图,按一个像素四个字节算,
3000 * 3000 * 4 / 1024 / 1024 = 34 M,想想看客户端一个应用程序的运行内存就十几M,你一下显示一个30多M的图片,直接crash了。
2.怎么解决?
这里就需要对图片进行二次采样。
原理:
BitmapFactory内部的图片解码,形成Bitmap是通过底层C/C++来实现的,有专门的图片界面库,可以通过参数来获取图片的尺寸,以及设置针对颜色加载的采样比率,采样比率就是把多个像素采样成一个像素,图片自然就变小了,最终传递给Java级别的对象,内存就变小了,图片也就变小了。
步骤:
第一步:在加载网络、文件的图片时,进行图片的解码BitmapFactory.decode(),只进行图片的尺寸获取,不进行实际的Bitmap创建,因此,第一次解码,不会占用太多的内存,可以获取图片的宽和高;
第二步:根据图片真实的宽高,以及客户端希望图片加载后的尺寸,进行一个计算,形成一种图片解码时缩小采样的一个数值,这个数值,可以直接运用在BitmapFactory上,加载到内存的Bitmap,就是变小的,内存是小图片的内容,不是原始图片的内存;
3.参数说明
3.1BitmapFactory中的Options说明
1)成员变量作为配置信息传给解码器
2)变量分为两部分:in开头的、out开头的
3)In开头的:用于个解码器传递参数
4)Out开头的:用于从解码器获取结果
3.2inSampleSize 采样比率
1)最好是2的幂,因为这种C语言解码效率最高
2)采样比率有一个官方的算法:以对角线的方式进行缩放比率的计算
/**
* 计算图片二次采样的采样率,使用获取图片宽高之后的Options作为第一个参数
* @param options
* @param reqWidth
* @param reqHeight
* @return
*
* --by Google
*/
private static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
//只有当请求的宽度、高度 > 0时,进行缩放
//否则,图片不进行缩放
if(reqHeight > 0 && reqWidth > 0){
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
}
return inSampleSize;
}
4.使用:
注释很详细了,不细说
/**
* 获取二次采样图片
* @param data
* @param requestWidth
* @param requestHeight
* @return
*/
public static Bitmap getDownDimensionBitmap(byte[] data, int requestWidth, int requestHeight){
Bitmap bitmap = null;
if(data != null){
//按照原始的图片尺寸,进行Bitmap的生存
//按照Bitmap生成,是按照图片的原始宽高,进行生成,并且每一个像素占用4个字节 ARGB
// ret = BitmapFactory.decodeByteArray(data, 0, data.length);
//采用二次采样(缩小图片尺寸的方式)
//1.步骤1 获取原始图片的宽高信息,用于进行采样的计算
//1.1创建Options ,给BitmapFactory的内部解码器设置参数
BitmapFactory.Options options = new BitmapFactory.Options();
//1.2设置inJustDecodeBounds 来控制解码器,只会进行图片宽高的获取,不会获取图片
//不占用内存,当使用这个参数,代表BitmapFactory.decodexxx()不会返回bitmap
options.inJustDecodeBounds = true;
//解码,使用options参数 设置解码方式
BitmapFactory.decodeByteArray(data, 0, data.length, options);
//2.步骤2 根据图片的真实尺寸,与当前需要显示的尺寸,进行计算,生成采样率
//2.1
//int picW = options.outWidth;
//int picH = options.outHeight;
//2.2准备 显示在手机上 256x128 128x64
//尺寸是根据程序需要来设置的
// int reqW = 256;
// int reqH = 128;
//2.3计算 设置 图片采样率
options.inSampleSize = calculateInSampleSize(options, requestWidth, requestHeight);//宽度的1/32 高度的1/32
//2.4开放 解码,实际生成Bitmap
options.inJustDecodeBounds = false;
//2.4.1 Bitmap.Config的说明
//要求解码器对于每一个采样的像素,使用RGB_565 存储方式
//一个像素,占用两个字节,比ARGB_8888笑了一半
//如果解码器不能使用指定配置,就自动使用ARGB_8888
options.inPreferredConfig = Bitmap.Config.RGB_565;
//2.4.2是一个过时的设置,可以及时清除内存
options.inPurgeable = true;
//2.5使用设置采样的参数,来进行 解码,获取bitmap
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
}
return bitmap;
}