Loading Large Bitmaps Efficiently

Images come in all shapes and sizes. In many cases they are larger than required for a typical application user interface (UI). For example, the system Gallery application displays photos taken using your Android devices’s camera which are typically much higher resolution than the screen density of your device.

Given that you are working with limited memory, ideally you only want to load a lower resolution version in memory. The lower resolution version should match the size of the UI component that displays it. An image with a higher resolution does not provide any visible benefit, but still takes up precious memory and incurs additional performance overhead due to additional on the fly scaling.

This lesson walks you through decoding large bitmaps without exceeding the per application memory limit by loading a smaller subsampled version in memory.

译:

图片有不同的形状与大小。在大多数情况下它们的实际大小都比需要呈现出来的要大很多。例如,系统的Gallery程序会显示那些你使用设备camera拍摄的图片,但是那些图片的分辨率通常都比你的设备屏幕分辨率要高很多。

考虑到程序是在有限的内存下工作,理想情况是你只需要在内存中加载一个低分辨率的版本即可。这个低分辨率的版本应该是与你的UI大小所匹配的,这样才便于显示。一个高分辨率的图片不会提供任何可见的好处,却会占用宝贵的(precious)的内存资源,并且会在快速滑动图片时导致(incurs)附加的效率问题。

Read Bitmap Dimensions and Type


The BitmapFactory class provides several decoding methods (decodeByteArray()decodeFile(),decodeResource(), etc.) for creating a Bitmap from various sources. Choose the most appropriate decode method based on your image data source. These methods attempt to allocate memory for the constructed bitmap and therefore can easily result in an OutOfMemory exception. Each type of decode method has additional signatures that let you specify decoding options via the BitmapFactory.Optionsclass. Setting the inJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting outWidthoutHeight and outMimeType. This technique allows you to read the dimensions and type of the image data prior to construction (and memory allocation) of the bitmap.

1
2
3
4
5
6
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;

To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it, unless you absolutely trust the source to provide you with predictably sized image data that comfortably fits within the available memory.

译:

BitmapFactory 类提供了一些decode的方法 (decodeByteArray()decodeFile()decodeResource(), etc.) 用来从不同的资源中创建一个Bitmap. 根据你的图片数据源来选择合适的decode方法. 那些方法在构造位图的时候会尝试分配内存,因此会容易导致OutOfMemory的异常。每一种decode方法都提供了通过BitmapFactory.Options 来设置一些附加的标记来指定decode的选项。设置 inJustDecodeBounds 属性true可以在decoding的时候避免内存的分配,它会返回一个null的bitmap,但是 outWidth, outHeight 与 outMimeType 还是可以获取。这个技术可以允许你在构造bitmap之前优先读图片的尺寸与类型。

Load a Scaled Down Version into Memory


Now that the image dimensions are known, they can be used to decide if the full image should be loaded into memory or if a subsampled version should be loaded instead. Here are some factors to consider:

  • Estimated memory usage of loading the full image in memory.
  • Amount of memory you are willing to commit to loading this image given any other memory requirements of your application.
  • Dimensions of the target ImageView or UI component that the image is to be loaded into.
  • Screen size and density of the current device.

For example, it’s not worth loading a 1024×768 pixel image into memory if it will eventually be displayed in a 128×96 pixel thumbnail in an ImageView.

To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSizeto true in your BitmapFactory.Options object. For example, an image with resolution 2048×1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512×384. Loading this into memory uses 0.75MB rather than 12MB for the full image (assuming a bitmap configuration ofARGB_8888). Here’s a method to calculate a sample size value that is a power of two based on a target width and height:

译:

通过上面的步骤我们已经知道了图片的尺寸,那些数据可以用来决定是应该加载整个图片到内存中还是加载一个缩小的版本。有下面一些因素需要考虑:

  • 评估加载完整图片所需要耗费的内存。
  • 程序在加载这张图片时会涉及到其他内存需求。
  • 呈现这张图片的组件的尺寸大小。
  • 屏幕大小与当前设备的屏幕密度。

例如,如果把一个原图是1024*768 pixel的图片显示到ImageView为128*96 pixel的缩略图就没有必要把整张图片都加载到内存中。

为了告诉decoder去加载一个低版本的图片到内存,需要在你的BitmapFactory.Options 中设置 inSampleSize 为 true 。For example, 一个分辨率为2048×1536 的图片,如果设置 inSampleSize 为4,那么会产出一个大概为512×384的bitmap。加载这张小的图片仅仅使用大概0.75MB,如果是加载全图那么大概要花费12MB(前提都是bitmap的配置是 ARGB_8888). 下面有一段根据目标图片大小来计算Sample图片大小的Sample Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}

Note: A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per the inSampleSize documentation.(文档是这样说的:If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 the number of pixels. Any value <= 1 is treated the same as 1. Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2.)假如inSampleSize为4,宽高尺寸会缩小到原来的四分之一,分辨率会缩小到原来的十六分之一。如果inSampleSize小于等于1,则其为1.

To use this method, first decode with inJustDecodeBounds set to true, pass the options through and then decode again using the new inSampleSize value and inJustDecodeBounds set to false:

译:

为了使用这个方法,首先需要设置 inJustDecodeBounds 为 true, 把options的值传递过来,然后使用inSampleSize 的值并设置 inJustDecodeBounds 为 false 来重新Decode一遍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}

This method makes it easy to load a bitmap of arbitrarily large size into an ImageView that displays a 100×100 pixel thumbnail, as shown in the following example code:

译:使用上面这个方法可以简单的加载一个任意大小的图片并显示为100*100 pixel的缩略图形式。像下面演示的一样:

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值