有很多原因会导致加载bitmap到你的程序需要一定的技巧:
·移动设备是很典型的一种资源有限的系统。Android设备的每个应用程序可以用的内存小到16MB。Android兼容定义文档(CDD),3.7节:虚拟机适配,其中给出了程序在不同屏幕尺寸和密度下所需的最小内存。程序应该在这个最小内存的限制下做优化。然而,需要记住的是,很多设备被配置成更高的限制。
·Bitmap占了很大的内存,尤其是rich images,比如相片。比如,Galaxy Nexus的摄像头生成的照片像素达到了2592x1936(5百万像素)。如果这个bitmap配置使用的是ARGB_8888(Android2.3前面的系统默认设置),那么加载这个图片需要的内存达到19MB,(2592*1936*4byte),一下就会消耗完某些设备上每个程序的内存限制。
·Android程序的UI经常需要很多bitmap一次性加载完成。这些组件,比如Listview,Gridview,和Viewpager,通常都会一次包含很多bitmap,并且还有很多潜在的在屏幕外需要加载的图片准备让手指的滑动展示出来。
教程1:高效地加载大图片
图片会有不同的现状和尺寸。在很多例子中,它们都比需要的大,典型的比如在程序UI中。比如:系统相册程序展示的用你的Android设备的摄像头拍出的相片,通常都会远大于你设备屏幕的分辨率。
既然你是在有限的内存在工作的,那么你自然是想在内存中加载更低分辨率的版本。这个更低分辨率的版本必须和UI组件显示的要匹配。更高分辨率的图片不会带来任何可见的益处,但是它还是会占用宝贵的内存,还会因为额外的图片缩放导致更多的性能上的开销。
本教程将让你学习解码大bitmap,而不会超过每个程序限定内存,这是通过在内存中加载更小的子样本版本。
读取bitmap的尺寸和类型
BitmapFactory类提供了集中解码的方法(decodeByteArray(),decodeFile(),decodeResources().等等),用来通过不同的资源创建bitmap。这些方法会试图为创建的bitmap分配内存,所以会很容易导致OutOfMemroy异常。每个解析的方法都有额外的标志去让你指定解析的参数,这是用BitmapFactory.Options类实现的。设置inJustDecodeBounds属性为true,可以避免在解码图片时分配 内存,通过设置bitmap的outWith,outHeight,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;
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
//计算最大的inSampleSize的值
// height and width larger than the requested height and width.
While((halfHeight/inSampleSize)>reqHeight&&(halfWidth/inSampleSize)>reqWidth){i
nSampleSize *= 2;}
return inSampleSize;}}
为了编码java.lang.OutOfMemery异常,在解码图片前检查图片的尺寸,除非你绝对信任这个资源会提供给你一个可预见的图片尺寸数据,并且可以很好的适用这些可用内存。
在内存中加载一个缩放的图片
现在既然知道了图片的尺寸,我们就可以以用它来决定是不是该加载这个完整的图片或者是加载一个处理过的版本。这里需要考虑几点:
·估计加载完整图片所需要的内存
·你可以用来加载图片的内存大小,同时需要满足你的程序其它的内存需求。
·要显示的ImageView的尺寸大小或者要加载图片的UI组件的大小
·当前设备的屏幕尺寸和密度
比如:没有必要在最终显示为128*96缩略图的ImageView中加载1024x768像素的图片到内存中。
为了让解码器生成图片的子样,来加载一个小版本到内存中,在你的BitmapFactory.Options对象中设置inSampleSize为true。例如,一个图片的分辨率是2048x1536,用inSampleSize为4来解码,会生成一个接近512x384的bitmap。加载这个图片占用的内存是0.75MB而不是12MB的全清图片(假设当前的bitmap的配置为ARGB_8888).下面是一个计算示例大小值,其取决于目标的宽度和高度。
提示:计算2的平方数的值是因为根据inSampleSize的文档,解码器使用了一个2的平方数的常量值,这个值非常接近2的平方数。
要使用这个方法,首先解码器要设置inJustDecodeBounns为true,将参数传递过去,然后使用一个新的inSampleSize的值,设置inJustDecodeBouns为false,从而再次解码。
public static Bitmap decodeSampleBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight){ //首先设置inJustDecodeBounds=true来检查图片尺寸 final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFacotry.decodeResource(res,resId,options); //计算inSampleSize options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight); //用inSampleSize设置来解码Bitmap options.inJustDecodeBounds = false; return BitmapFactory.decodeResources(res,resId,options); }
这个方法使得加载一个任意尺寸的bitmap到一个显示为100x100像素的缩略图的ImageView中变得很简单。如下面的代码所示:
mImageView.setImageBitmap(decodeSampleBitmapFromResource(getResource(),R.id.myimage,100,100));
你可以用类似的步骤去解码其它的资源,但是需要用适当的BitmapFactory.decode*方法去代替。