您的位置:首页 > 移动开发 > Android开发

android开发之Bitmap的高效加载

2016-10-16 20:45 232 查看
今天给大家介绍一下如何高效的加载一个Bitmap,在后面的文章里会给大家介绍一下如何自己写一个功能完备Image Loader,这里是ImageLoader的前期准备工作。

由于Bitmap的特殊性和Android应用的内存资源有限,在加载Bitmap时,很容易出现内存溢出(OOM)。

在介绍如何高效的加载一个Bitmap之前,我们先说一下如何加载一个Bitmap,Bitmap在Android中指的时一张图片,可以使png格式也可以是jpg等其他常见的图片格式。那么如何加载一个Bitmap呢,相信大家都知道,BitmapFactory类提供了四类方法:decodeFile、decodeResource、decodeStream、和decodeByteArray,分别用于支持从文件系统、资源、输入流、以及字节数组中加载出一个Bitmap对象,上面四种方法中的decodeFile和decodeResource又间接调用了decodeStream方法。

那现在我们说一下高效的加载Bitmap的核心思想,其实也很简单就是采用BitmapFactory.Options来加载所需尺寸的图片。为什么要加载成所需要的尺寸呢,我们假设使用ImageView来显示图片,但是ImageView的大小没有原始图片那么大,那么,我们把整张图片加载进来设置给ImageView是不是就有些没有必要了,确实如此,我们只需要加载ImageView需要的大小的图片即可。这时候就要用到我们上面说到的BitmapFactory.Options了。通过BitMapFactory.Options就可以按照一定的采样率来加载缩小后的图片,讲缩小后的图片设置给ImageView,这样的话既不会浪费内存资源,并且也降低了发生OOM的概率。BitmapFactory提供的加载图片的四种方法都可以支持BitmapFactory.Options参数,通过他们就可以很方便的对一个图片进行采样缩放了。

在上面,我们刚刚提到了采样率,也许大家对采样率不太了解。在使用Bitmap.Options来缩放图片时,最重要的参数就是采样率,即inSampleSize参数。

当inSampleSize为1时,则代表采样后图片的大小与图片原始大小相同;当InSampleSize大于1,例如为2时,那么采样后的图片的宽高都为原始图片的1/2,而像素数时原始图片的1/4,当然其占有的内存也就缩小到了原始图片的1/4,。如果采样率小于1时,其作用相当于1,即无缩小效果。可以发现采样率只有是大于1的整数时才会有缩小的效果,并且采样率同事作用于图片的宽高,导致缩小后的图片以2的次方的形式缩小,即缩小比例为1/(inSampleSize的2次方),比如inSampleSize为4,那么缩小比例就是1/16。

另外还有一点注意的是,在官方文档中建议inSampleSize的取值总是2的指数的形式,例如1,2,4,8,16...等等,如果传递给inSampleSize的值不是2的指数的形式,系统会向下取整并选择一个最接近2的指数来代替。

刚才我们说到如果给ImageView一个比它尺寸大的图片会浪费内存资源,增大OOM的几率。但是如果我们缩小后的图片小于ImageView的大小呢,这样的图片就会被拉伸导致模糊不清。可以看到有效大缩放控制BitmapFactory的加载是多么的重要。

上面说了这么多的采样率的介绍,那么到底怎样才能获得采样率呢。其实也很简单,大体流程如下:

1)将BitmapFactory.Options的inJustDecodeBounds参数设置为true,并加载图片。

当把inJustDecodeBounds设置为true之后,BitmapFactory在加载图片时,只会解析图片的原始的宽高信息,并不会真正的去加载一张图片。

2)从BitmapFactory.Options中取出图片的原始宽高信息。

在程序中,宽高分别对应着outWidth和outHeight属性。

3)根据采样率的规则并结合目标View所需要的大小计算出inSampleSize的值。

4)将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。

 

将上面的步骤用代码实现如下:

public static Bitmap decodeSampleBitmapFromResource(Resources res, int resId, int requestWidth, int requestHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);

options.inSampleSize = calculateInSampleSize(options, requestWidth, requestHeight);

options.inJustDecodeBounds = false;

return BitmapFactory.decodeResource(res, resId, options);
}

private static int calculateInSampleSize(BitmapFactory.Options options, int requestWidth, int requestHeight) {
final int height = options.outHeight;
final int width = options.outWidth;

int inSampleSize = 1;
if (height > requestHeight || width > requestWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;

while ((halfWidth / inSampleSize) >= requestWidth && (halfHeight / inSampleSize) >= requestHeight) {
inSampleSize = inSampleSize * 2;
}
}
return inSampleSize;
}

使用起来也是非常的简单,给ImageView设置一个100x100像素的Bitmap,代码如下:
iv.setImageBitmap(decodeSampleBitmapFromResource(getResources(), R.id.imgv, 100, 100));
上面的例子只使用了BitmapFactory的decodeResource方法,其他的三个方法处理的方式类似,在后面会有详细的介绍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: