由于bitmap的特殊性以及Android对应用所施加的内存限制,导致加载bitmap的时候很容易出现内存溢出。下面这个异常信息在开发中应该时常遇到:
java.lang.OutofMemoryError : bitmap size exceeds VM budget
因此高效地加载bitmap是一个很重要也很容易被开发者忽视的问题。
1.如何高效加载bitmap
BitmapFactory类提供了四类方法:decodeFile,decodeResource,decodeStream 和 decodeByteArray ,分别用于支持文件系统,资源,输入流,及字节数组中加载出一个Bitmap对象,其中decodeFile 和 decodeResource 又间接调用了decodeStream方法,这四类方法最终是在android底层实现的,对应着BitmapFactory类的几个native方法。
如何高效的加载Bitmap ? 那就是采用BitmapFactory.options来加载所需尺寸的图片。通过BitmapFactory.Options就可以按一定的采样率加载缩小后的图片,将缩小后的图片在ImageView中显示,这样就会降低内存的占用从而在一定程度上避免OOM。
2.什么是采样率
通过BitmapFactory.Options来缩放图片,主要是用到了它的inSampleSize参数,即采样率。当inSampleSize为1时,采样后的图片大小为原始大小,当inSampleSize大于1时,比如2,那么采样后的图片其宽高均为原图大小的1/2,像素数为原图的1/4,占有内存的大小也为原图的1/4。有一种特殊情况,当inSampleSize小于1时,其作用相当于1,即无缩放效果。另外官方文档指出,inSampleSize的值应该总是为2的指数,比如:1,2,4,8,16 等等。如果未见传递的inSampleSize 不为2的指数,那么系统会向下取整并选择一个最接近的2的指数来代替。
3.采样流程
(1)将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。
(2)从BitmapFactory.Options中取出图片的原始宽高信息。他们对应于outWidth 和 outHeight 参数。
(3)根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize.
(4)将BitmapFactory.Options的inJustDecodeBounds 参数设为false,然后重新加载图片。
4.采样流程实现程序
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.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;
if(height > reqHeight || width > reqWidth){
final int halfHeight = height/2;
final int halfWidth = width/2;
while((halfHeight /inSampleSize) >reqHeight
&& (halfWidth /inSampleSize) >halfWidth){
inSampleSize *=2;
}
}
return inSampleSize;
}
PS: 本文是对《Android 开发艺术探索》一书的阅读笔记,想了解原文 请自行搜索。