Android对单个应用的内存分配是有限制的,比如16M,这样就导致在加载Bitmap时很容易就导致OOM(out of menory 内存溢出)。
为了解决Bitmap对内存造成的困扰,我们需要比较高效的加载Bitmap的策略。
对于Bitmap高效加载,我们先谈谈Bitmap的加载。
Bitmap在Android中指的是一张图片。加载一张图片我们使用的是Android中 BitmapFactory 类中的方法。BitmapFactory中提供了四类方法:decodeFile,decodeResoure,decodeStream,decodeByteArry。分别可以从文件系统,资源,输入流,及字节数组中加载一个Bitmap对象。其中decodeFile,decodeResoure又间接调用decodeStream方法。这四类方法最终是在Android的底层实现的,对应着BitmapFactory类的几个native方法。
再来谈谈高效加载图片,核心思想就是改变Bitmap的采样率--inSampleSize。因为我们很多时候显示的图片并不需要加载图片的原始尺寸,我们只需要将图片按一定比例缩小后显示即可。这样在一定程度上降低加载图片的内存,减少了OOM的风险。
高效加载Bitmap的一般步骤如下:
1.将BitmapFactory.options的inJustDecodeBonds参数设置为true。
2.从BitmapFactory.options中取出图片的原始宽高信息。
3.根据目标View的大小结合原始宽高信息得出相对应的采样率,并设置还给BitmapFactory.options的inSampleSize。
4.将BitmapFactory.options的inJustDecodeBonds参数设置为true。并重新加载图片。
inJustDecodeBonds参数,设置为true时, BitmapFactory只会加载 图片的原始宽高信息,并不会真正加载图片,所以这个操作是轻量级的。
注意:BitmapFactory获取的图片宽高信息和图片的位置以及程序运行的设备有关,比如同一张图片放在不同的drawable目录下或者程序运行在不同屏幕密度的设备上,都可能导致BitmapFactory获取到不同的结果,和Android的资源加载机制有关。
inSampleSize,即采样率,通过对 inSampleSize的设置,对图片的像素的高和宽进行缩放。
当inSampleSize=1,即采样后的图片大小为图片的原始大小。小于1,也按照1来计算。 当inSampleSize>1,即采样后的图片将会缩小,缩放比例为1/(inSampleSize的二次方)。inSampleSize的取值应该总是2的指数,如1,2,4,8等。如果外界传入的inSampleSize的值不为2的指数,那么系统会向下取整并选择一个最接近2的指数来代替。比如3,系统会选择2来代替(开发建议)。
注意:通常需要根据图片宽高实际的大小/需要的宽高大小,分别计算出宽和高的缩放比。但应该取其中最小的缩放比,避免缩放图片太小,到达指定控件中不能铺满,需要拉伸从而导致模糊。
下面是对上面四个步骤的实现:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//加载图片
BitmapFactory.decodeResource(res,resId,options);
//计算缩放比
options.inSampleSize = calculateInSampleSize(options,reqHeight,reqWidth);
//重新加载图片
options.inJustDecodeBounds =false;
return BitmapFactory.decodeResource(res,resId,options);
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) {
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;
if(height>reqHeight||width>reqWidth){
int halfHeight = height/2;
int halfWidth = width/2;
//计算缩放比,是2的指数
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
到这里就可以方便的加载缩放后的图片了:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.mipmap.bitmap,100,100);