Android大尺寸图片加载问题

对于大尺寸图片加载,如何正常加载显示且不发生OOM?

  • 图片采样,应用场景:图片全部显示在控件上
  • 图片按区域加载,应用场景:高清巨图,不允许压缩,如世界地图、清明上河图

图片采样

图片采样的方法,是根据需要显示图片控件的大小对图片进行压缩显示。

主要用法

1、根据控件的宽高与原图的宽高的比例,计算出BitmapFactory.OptionsinSampleSize值。例如:

public static int calculateInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options) {
    if (reqHeight == 0 || reqWidth == 0) {
        return 1;
    }

    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;


    if (height > reqHeight || width > reqWidth) {
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and
        // keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    Log.d(“TAG”, "calculateInSampleSize: " + inSampleSize);
    return inSampleSize;
}

第一、二个参数分别是显示控件的宽度和高度,第三个参数是设置了inSampleSize的BitmapFactory.Options对象,设置inSampleSize为2的幂是因为解码器最终会对非2的幂的数进行向下处理,获取到最靠近2的幂的数。详情参考inSampleSize的文档。

2、然后在解析图片文件时,传入这个BitmapFactory.Options。例如

public static Bitmap getFitSampleBitmap(String filePath, int width, int height) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath, options);
    //计算inSampleSize方法
    options.inSampleSize = calculateInSampleSize(width, height, options);
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(filePath, options);
}

第一个参数是图片存放的路径,第二、三个参数是显示控件的宽度和高度

测试过程,查看Memory使用情况

1、没有压缩处理,内存占用106M左右
没有压缩.png

2、inSampleSize=2,内部占用46M左右
inSampleSize=2.png

从上面对比可以看出,经过图片的采样压缩,内存使用大大降低,减少OOM的发生。

具体Demo可以参考Android 多种方式正确的加载图像,有效避免oom

图片按区域加载

对于世界地图等高清巨图,如果不压缩,那么按照原图尺寸加载,屏幕肯定不够,并且考虑内存的情况,不可能一次性将整图加载到内存中,否则很容易发生OOM。如何解决这种问题,可以采用的方法:局部加载,让图片按区域加载,用到Android提供的类:

BitmapRegionDecoder

主要用法:

1、创建BitmapRegionDecoder对象

BitmapRegionDecoder提供了一系列的newInstance方法来构造对象,传入需要处理的图片,支持传入文件路径,文件描述符,文件的inputstrem等。

例如:

BitmapRegionDecoder bitmapRegionDecoder =
  BitmapRegionDecoder.newInstance(inputStream, false);

2、显示指定的区域

bitmapRegionDecoder.decodeRegion(rect, options);

参数一是一个rect,指定显示的区域,参数二是BitmapFactory.Options,可以控制图片的inSampleSize, inPreferredConfig等。

具体Demo可以学习Android 高清加载巨图方案 拒绝压缩图片,加上手势移动,动态修改显示的区域。

调试DEMO时,发现存在一个问题,初始化显示setInputStream时,图片只显示一角,手势移动没有反应。

通过Debug,查到BitmapFactory.Options获取图片的宽高有问题,修改方式是直接通过BitmapRegionDecoder.getWidth和getHeight获取宽高。

参考资料

1、Android 多种方式正确的加载图像,有效避免oom

2、Android 高清加载巨图方案 拒绝压缩图片

3、Android图片尺寸与质量压缩你用对了吗?inSampleSize与quality的浅析

你可能感兴趣的:(Android)