本文主要介绍一些通用的处理和加载Bitmap对象的方法.这些技术可以使的应用的UI更加流程并且避免消耗过多的内存.如果不注意这些,Bitmaps会迅速消耗应用内存进而导致程序Crash.
错误 : java.long.OutofMemoryError : bitmap size exceeds VM budget.
图片有不同的形状和大小.在大多数情况下他们的实际尺寸都比需要呈现的尺寸要大很多.考虑到程序是在有限的内存下工作.理想情况是只需要在内存中加载一个低分辨率的图片即可.
Bitmap类提供了一系列decode方法(decodeByteArray(),decodeFile(),decodeResource(),etc)用来从不同的资源中创建一个Bitmap对象 . APIs 如下 :
根据你的图片数据源来选择合适的decode方法,那些方法在构造位图时会尝试分配内存,因此容易导致OutOfMemory的异常 . 每一种decode方法都提供了通过BitmapFactory.Options来设置一些标记来指定decode 的选项. 设置 inJustDecodeBounds 属性为 true 可以在decoding的时候避免内存分配 , 他会返回一个null的bitmap对象,但是outWidth,outHeight与outMimeType还是可以获取到的 . 这种技术允许你在构造bitmap之前获取图片的尺寸和类型.
获取到图片的的实际尺寸后可以结合我们需要显示的尺寸来及算出缩放比例 . BitmapFactory.Options 对象的inSampleSize 变量就是表示加载图片时的缩放比例 . 如此一来我们就可以按照我们的需求来加载一个降低分辨率后 的图片 , 节省不必要的内存开销. 同时可以在很大程度上避免OutofMemory错误.
inSampleSize 解析:
inSampleSize 是一个用来表示加载图像缩放比例的 int 类型的值 , Android系统对于它的处理分以下两种情况 :
(1) inSampleSize > 1 时 :
加载图片时会对图片进行二次采样(修改加载图片尺寸) , inSampleSize 大小是在解码位图中对应于尺寸中长度 / 宽度的缩放比例。例如 , inSampleSize = 4 返回的图片的长度 和 宽度上的像素数量都将会变为原来的四分之一(1/4) . 整个图片的像素数时实际图片像素数的十六分之一(1/(4 * 4)) . 对应尺寸关系如下
假设 inSampleSize = 4. 则有如下关系 :
A. 加载图片的长度 = 图片真实长度 / 4;
B. 加载图片的宽度 = 图片真实长度 / 4;
C. 加载图片的尺寸 = 图片真实尺寸 / 16;
(2) inSampleSize <= 1 时 :
对于这种情况Android系统会将inSampleSize当做1对待 .
注意 : inSampleSize 都是2 的幂 如果设置的不是2 的幂则系统会用最接近它值得2 的次幂来代替.
inJustDecodeBounds 解析 :
inJustDecodeBounds 是一个表示是否为图片分配内存的布尔值 .因此可以利用此标记获取图片尺寸.
(1) inJustDecodeBounds = true : decoder 加载图片时不会再内存中分配空间 , 返回一个null对象.但是回返回图 片的真实尺寸和类型(在BitmapFactory.Options对象中).
(2) inJustDecodeBounds = false : decoder 加载图片时会为Bitmap分配内存.
计算图片缩放比例代码:
/**
* 计算图片缩放比例
* @param options Options 对象
* @param reqWidth 显示的宽度
* @param reqHeight 显示的高度
* @return
* 缩放比例 : 宽高都会缩放inSampleSize.
*/
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
// 获取图片的实际尺寸
final int height = options.outHeight;
final int width = options.outWidth;
// 初始缩放比为 1,即不进行缩放.
int inSampleSize = 1;
// 计算缩放比.
if (height > reqHeight || width > reqWidth){
final int halfH = height / 2;
final int halfW = width / 2;
while((halfH / inSampleSize) > reqHeight && (halfW / inSampleSize) > reqWidth){
inSampleSize *= 2;
}
}
return inSampleSize;
}
加载指定大小的图片到内存中 :
/**
* 加载指定大小的图片
* @param res 资源
* @param resId 资源id
* @param reqWidth 显示宽度
* @param reqHeight 显示高度
* @return
* 处理后的图片
*/
public static Bitmap decodeSampledBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight){
// 1. 获取实际尺寸
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res,resId,options);
// 2. 计算 inSampleSize
options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
// 3. 加载缩放后的图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res,resId,options);
}