Loading Large Bitmaps Efficiently
如何优化加载大位图
Images come in all shapes and sizes.In many cases they are larger than required for a typical application userinterface (UI). For example, the system Gallery application displays photostaken using your Android devices's camera which are typically much higherresolution than the screen density of your device.
Given that you are working with limited memory, ideally you onlywant to load a lower resolution version in memory. The lower resolution versionshould match the size of the UI component that displays it. An image with ahigher resolution does not provide any visible benefit, but still takes upprecious memory and incurs additional performance overhead due to additional onthe fly scaling.
This lesson walks you through decoding large bitmaps without exceedingthe per application memory limit by loading a smaller subsampled version inmemory.
图片有各种形状和大小。在很多情况下,这些图片的大小超过我们应用程序的需要,例如用手机拍摄的图片。
假如,你的应用程序只有有限的内存容量,最理想的做法是在内存中加载低分辨率的图像版本。低版本的图像需要适应设备屏幕的大小。一张高分辨率的图片不仅不会改善显示质量,而且还会影响应用程序的性能。
这个课程将讲述如何将一些大的位图压缩成少于应用程序使用内存容量的小图片。
ReadBitmap Dimensions and Type
读取位图的尺寸和类型
The BitmapFactory
class providesseveral 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 andtherefore can easily result in an OutOfMemory
exception. Each typeof decode method has additional signatures that let you specify decodingoptions via the BitmapFactory.Options
class. Setting theinJustDecodeBounds
property to true
while decoding avoidsmemory allocation, returning null
for the bitmap objectbut setting outWidth
, outHeight
and outMimeType
. Thistechnique allows you to read the dimensions and type of the image data prior toconstruction (and memory allocation) of the bitmap.
BitmapFactory类提供了几个解码方法能够从各种源文件中创建Bitmap对象,例如(decodeByteArray(), decodeFile(),decodeResource()等)。我们需要根据图片的格式选择合适的解码方法。这些方法都尝试去为创建好的Bitmap对象分配内存,但是这样做会很容易导致OutOfMemory
的异常。每种类型的解码方法,你都需要通过BitmapFactory.Options
类来指定参数值。将inJustDecodeBounds
参数值设定为true的时候,解码过程中将不为对象分配内容,但是会将图片的尺寸等信息通过BitmapFactory.Options
对象的outWidth
, outHeight
和 outMimeType
值反馈。
如下代码所示:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
//解码完成后,outWidth
,outHeight
和outMimeType
值将包含图片的尺寸大小 int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;
To avoid java.lang.OutOfMemory
exceptions, check thedimensions of a bitmap before decoding it, unless you absolutely trust thesource to provide you with predictably sized image data that comfortably fitswithin the available memory.
为了避免java.lang.OutOfMemory
异常,需要在真正解码之前,通过上面代码描述的方法获取图片的真实尺寸。
Load aScaled Down Version into Memory
将一个压缩版本加载到内存中
Now that the image dimensions are known, they can be used todecide if the full image should be loaded into memory or if a subsampledversion should be loaded instead. Here are some factors to consider:
至此,你可以通过图片的尺寸来觉得是否使用一个一个压缩的版本来加载到内存中。下面有一些因素,你需要考虑到:
· Estimated memory usage of loading the fullimage in memory.
· Amount of memory you are willing to commit toloading this image given any other memory requirements of your application.
· Dimensions of the target ImageView
or UIcomponent that the image is to be loaded into.
· Screen size and density of the currentdevice.
1、
预估加载整个图片后,需要占用的内存大小;
2、
应用程序关于加载图片时使用内存大小的需求;
3、
存放图片的
ImageView
或者
UI
组件的尺寸;
4、 屏幕大小。
To tell the decoder to subsample theimage, loading a smaller version into memory, set inSampleSize
totrue
in your BitmapFactory.Options
object. For example,an image with resolution 2048x1536 that is decoded with an inSampleSize
of 4 produces abitmap of approximately 512x384. Loading this into memory uses 0.75MB ratherthan 12MB for the full image (assuming a bitmap configuration of ARGB_8888
). Here’sa method to calculate a sample size value that is a power of two based on atarget width and height:
为了能够将图片压缩,你需要将
inSampleSize
的值设定为
true
。下面是计算压缩比例的例子:
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 valueis calculated because the decoder uses a final value by rounding down to thenearest power of two, as per the inSampleSize
documentation.
To use this method, first decode with inJustDecodeBounds
set to true
, pass theoptions through and then decode again using the new inSampleSize
value and inJustDecodeBounds
set to 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); }
This method makes it easy to load a bitmap of arbitrarily largesize into an ImageView
that displays a100x100 pixel thumbnail, as shown in the following example code:
mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
You can follow a similar process to decode bitmaps from othersources, by substituting the appropriateBitmapFactory.decode*
method as needed.