Android如何高效的加载图片(1)---加载大尺寸图片

完美的去加载bitamap,不但可以增加用户体验度,还能避免内存溢出。加载bitmap,一部小心就会导致应用crash.报出下面的异常。

java.lang.OutofMemoryError: bitmap size exceeds VM budget.

如果需要加载图片比较大。很容易造成内存溢出,如何去防止内存溢出呢。

读取bitmap的尺寸和类型

BitmapFactory类提供了很多decoding方法(decodeByteArray(), decodeFile(), decodeResource()等)去加载bitmap从不同的数据源,根据图片的来源去选择合适的方法。这些方法在会占用很多内存去构造bitmap,因此,容易导致oom异常。我们如何去避免OOM异常呢。
每个方法有BitmapFactory.Options类,这个类中有个成员变量为inJustDecodeBounds,当我们设置它为true时,再去加载图片,则会避免OOM。这样做后,返回的bitmap为null。但是会返回图片的outWidth, outHeight 和outMimeType。那大家就疑惑了,我们就是为了创建bitmap,而现在返回的bitmap为null,有何用。其实这个就是为了在创建bitmap前获取图片的尺寸和类型。
为了避免OOM,在创建bitmap前,我们先去检查它的尺寸,除非,你绝对的有把握,现在app的内存完全足够去加载你想要decode的图片。
下面是获取图片尺寸和类型的代码:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

###加载缩放后图片到内存
现在,我们获取到了图片的尺寸,我们就可以知道我们是直接加载原图到内存,还是缩放后再去加载。
距离来说:现在有张1024x768像素的图片,但是我们最终只需要展示一张128x96是像素缩略图即可。

现在有张图片,原图大小为2048x1536,通过inSampleSize设置缩放4倍后,图片大小为512x384,加载原图需要12MB(假设图片的配置为ARGB_8888),而加载缩放后的图片需要0.75MB。下面这个方法就是的来计算缩放比例:

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 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;
        }
    }

    return inSampleSize;
}

我们发现上面的代码中计算缩放比取值用2的倍数。因为最终构建bitmap时,取值为2的倍数,舍尾。

利用上面这个方法,首先设置inJustDecodeBounds为true,获取到缩放比inSampleSize 的值后,再设置inJustDecodeBounds为false,最终利用bitmapFactory类中的方法去获取bitmap.
下面为获取bitmap的完整过程。

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

利用上面的方法,如果现在我们的需求为加载一张100x100的缩略图到ImageView中。代码为下:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

可以看到,经过以上的封装,我们可以非常容易的去加载任意尺寸的图片。

欢迎阅读下一篇Android中Bitmaps 处理详解(2)—在ui线程中处理Bitmaps

你可能感兴趣的:(Java,Android)