Android使用BitmapFactory高效加载大图,防止OOM

基于以上,当我们决定是把原图加载进内存还是压缩图加载进内存的时候,有几点是需要考虑的:

  • 大概的估算一下这张图片占用多少内存

  • 展示图片的控件的实际像素大小。

  • 考虑一下当前设备的屏幕尺寸和屏幕分辨率。

这里我简单的说一下第二点和第三点。假设使用ImageView进行加载图片,很多时候ImageView没有图片的尺寸那么大,这个时候你把原始图片加载进来再设置给ImageView,是很浪费内存的,而且没必要,因为ImageView没有办法加载出原始的图片。第三点和资源的加载机制有关,比如同一张图片放在不同的drawable目录下,通过BitmapFactory获取的宽、高都不尽相同。

BitmapFactory

接下来聊一聊BitmapFactory,它提供了4类方法,分别是:decodeResource、decodeStream、decodeFile和decodeByteArray,分别对应着从资源加载出Bitmap对象、从输入流加载出Bitmap对象、从文件加载出Bitmap对象和从字节数组加载出Bitmap对象,我们可以根据图片的来源选择合适的加载方法。这些方法都会为Bitmap分配内存,那就有可能发生OOM,想象一下一张分辨率超高的图片加载进内存了!那有什么办法可以避免呢。这时候BitmapFactory.Options就要上场了,将它的属性inJustDecodeBounds设置为true就可以让解析方法不给Bitmap分配内存,也就能防止OOM,返回值也不是实际的bitmap,而是null,但是我们还是可以查询图片的相关信息比如宽、高。

 BitmapFactory.Options bmOptions = new BitmapFactory.Options();
 // 值设为true那么将不返回实际的bitmap,也不给其分配内存空间这样就避免内存溢出了。但是允许我们查询图片的信息这其中就包括图片大小信息
 bmOptions.inJustDecodeBounds = true;
 BitmapFactory.decodeFile(filePath, bmOptions);
 int photoW = bmOptions.outWidth;
 int photoH = bmOptions.outHeight;

那我们怎么对图片进行压缩呢?主要用到了BitmapFactory.Options的inSampleSize参数。当inSampleSize的值为1的时候,采集后的照片大小和原图一致,当inSampleSize为2时,采集后的照片大小是原图的1/2,像素值是原图的1/4。假设原图是800 * 1280的,那么它占用的内存大小是800 * 1280 * 4(假设是ARGB8888格式),将inSampleSize设置为2,那么采集到的图片内存大小是 400 * 640 * 4。下面的方法可以根据提供的宽、高算出合适的inSampleSize值:

public static int calculateInSampleSize(BitmapFactory.Options options,
        int reqWidth, int reqHeight) {
    // 源图片的高度和宽度
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;
    if (height > reqHeight || width > reqWidth) {
        // 计算出实际宽高和目标宽高的比率
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);
        // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
        // 一定都会大于等于目标的宽和高。
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }
    return inSampleSize;
}

将期望的宽、高传入到这个方法中,就可以得到合适的inSampleSize值。之后再从新解析一遍图片,使用这个新获取到的inSampleSize值,并把inJustDecodeBounds设置为false,就可以得到压缩后的图片了。 完整的代码如下:

public static Bitmap getDecodeBitmapFromFile(String fileName,
                                         int reqWidth, int reqHeight) {
        // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(fileName, options);
        // 调用上面定义的方法计算inSampleSize值
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        // 使用获取到的inSampleSize值再次解析图片
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(fileName, options);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值