Android中高效的显示图片之一 ——加载大图

时间:2023-02-01 21:03:24
在网上看了不少文章,发现还是官方文档介绍最详细,把重要的东西简单摘要出来。详细可看官方文档地址 (  http://www.bangchui.org/read.php?tid=9 ) 。


在应用中显示图片,如果不多加小心,很容易就会使应用因为异常“java.lang.OutofMemoryError:bitmap size exceeds VM budget”而导致crash。在android中加载图片需要一定的技巧性,主要是因为:

1.通常设备资源有限,安卓设备给每个应用只分配16M的空间。当然很多设备都为应用设置了更高的内存,下面这个文档的3.7节中有介绍不同屏幕设备应用需要的最小内存(http://source.android.com/compatibility/downloads.html)。

2.图片通常很占内存。例如Galxy Nexus相机分辨率高达2592x1936,如果图片配置为ARGB_8888(2.3以后默认配置),加载一张照片就需要19M内存(2592x1936x4 bytes),会很快消耗完一个应用的内存。

3.安卓的UI中通常需要一下加载多张图片,如ListView,ViewPager。


本文主要介绍怎么高效地加载大图而不至于使你的应用内存溢出。大图通常会比屏幕尺寸大很多,然而加载一个高分辨率的图片并不能带来多少视觉体验,通过下面两步可以加载一个压缩尺寸的图片就节省内存。

第一步,读取图片尺寸和类型。

BitmapFactory类提供了多个decoding方法(decodeByteArray(), decodeFile(), decodeResource(), 等)使可以通过各种数据源创建Bitmap,这些方法会在bitmap构造时申请内存,对于大图,这样会很容易导致内存溢出。每种decode方法都有一个额外的标志参数BitmapFactory.Options,设置它的inJustDecodeBounds属性为true,可以避免decode时分配内存,返回的Bitmap对象是一个null值,但能得到图片的原始高度、宽度和格式(保存在option的outWidth,outHeight,outMimeType中)。通过这种方式,可以在构造图片前获得图片属性。下面是示例代码:

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;

为了避免内存溢出,最好在加载图片前都对尺寸做检查,除非你保证源图片没有大图。

第二步,加载缩小后的图片到内存。

现在知道了原图片的尺寸,根据实际情况决定你要加载它缩小多少倍后的图片。例如你用一个128x96的ImageView显示一张1024x768的原图,根本没有必要把原图读加载到内存。加载一张缩小后的图片到内存,只需要把BitmapFactory.Options对象的inSampleSize设为true,然后给inSampleSize设一个值就行了(可以理解inSampleSize为n,图片就缩小到1/n大小)。

下面是一段加载图片的一段示例代码

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) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}


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