Android-如何高效的载入大图片

转载请标明出处:http://blog.csdn.net/goldenfish1919/article/details/37654697

原文:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

图片可以有各种各样的形状和大小。很多时候,他们会比UI需要的要大。比如说系统的画廊应用,它用来展示设备上用摄像头拍摄的照片,这些照片的分辨率一般会远远高于设备屏幕的密度。

假如你可用的内存是有限的,理想的情况是你只需要在内存中载入一个低分辨率的版本。这个低分辨率的版本应该跟展示它的UI的大小相匹配。过高分辨率的图片并不会提供任何视觉上的好处,但是,却仍会占据宝贵的内存,带来因缩放导致的额外性能开销。

本文带你学习如何解码大的图片,在内存中载入小的子副本,使它不会超过应用的内存限制。

获取Bitmap的尺寸和类型


BitmapFactory类提供了很多解码方法(decodeByteArray(), decodeFile(), decodeResource(),等)用来从各种数据源创建一个Bitmap。要根据你的图片的数据来源选择最合适的解码方法。这些方法会尝试给构造出来的bitmap分配内存,因此很容易就会导致OutOfMemory。每一种类型的解码方法都有一个额外的签名参数用来让你通过BitmapFactory.Options类指定解码选项。在解码的时候,把inJustDecodeBounds属性设置成true会避免内存分配,返回的结果bitmap是null,但是却设置了outWidth, outHeight and outMimeType。这种技术允许你在构造bitmap(并分配内存)之前能获取图片的尺寸和类型。

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;
为了避免java.lang.OutOfMemory,在解码图片之前,要检查bitmap的尺寸,除非你能绝对信任图片的来源,它能给你提供更可用内存相匹配的精确尺寸的图片,载入一个缩小比例的版本到内存。

既然已经知道了图片的尺寸,这可以用来决定是否要把完整的图片载入内存还是只需要载入一个子副本。


下面是要考虑的因素:

估算下图片完全载入所需要的内存
在保证你应用所需的其他内存的前提下,你愿意给这个图片分配多少内存
展示图片的目标ImageView或者是UI组件的尺寸
当前设备的屏幕大小和密度

比如:如果一个图片要展示在128x96像素的ImageView上,载入1024x768像素的图片到内存是不值当的。
为了告诉解码器生成图片的子副本,载入一个小版本到内存,在BitmapFactory.Options中把inSampleSize设置成true。
比如:一个图片的分辨率是2048x1536,如果用inSampleSize是4来解码,得到的图片大概是512x384。
把这个图片载入内存会占用0.75M而不是载入完整图片时候的12M(假设图片是ARGB_8888)。
下面是一个计算inSampleSize值的方法,返回值是基于目标宽度和高度的2的幂次方:

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    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的幂次方,这是因为解码器最终使用的值是最接近的2的幂次方,inSampleSize文档有说明。

要使用这个方法,首先把inJustDecodeBounds设置为true解码一次,然后设置inSampleSize和inJustDecodeBounds为false再解码一次:
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));
你可以按照这种处理方式从不同的数据源来解码图片,只需要替换成相对应的BitmapFactory.decode*就可以了。

你可能感兴趣的:(Android-如何高效的载入大图片)