在android应用程序中,你会遇到因为加载图片而导致的内存溢出,因此在加载图片的时候你需要格外的注意。首先我们分析一下内存溢出的几个原因:
- 移动终端系统一般是有限的内存资源,而android系统为每一个程序提供16M的内存空间。
- 图片资源非常占用系统资源,尤其是高清图片,举例来说三星Galaxy Nexus照相的分辨率能达到2592x1936像素,假设加载时采用ARGB—888格式,系统将占用(2592x1936X4)bytes,这将大大超出系统分配内存
- 在android应用中可能会经常出现在一屏幕上显示多个图片的情况,如listview,GridView,viewpage等,这将大大的消耗内存资源。
如何解决以上的问题呢 ?
提前预读取图片尺寸和类型
BitmapFactory
类提供了许多创建图片的方法( (decodeByteArray()
, decodeFile()
,decodeResource()
, etc),根据你图片资源的不同选择对应的方法,在这些方法中系统会尝试去为图片分配内存,这样容易导致OOM,然而每一个方法提供一个额外的参数 BitmapFactory.Options,
设置inJustDecodeBounds
为true,在创建图片是则会避免去分配内存,同时返回一个null对象,但图片的高度,宽度,MIME等信息都能获取,通过这种方式去读取图片信息,避免分配内存。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType; |
要避免OOM,在创建图片之前检查其大小是非常必要的,除非你明确知道图片不会超出你可用的内存。
加载缩小后的图片到内存
假设我们已经知道图片的尺寸信息,这样我们就可以决定是将图片全部加入到内存中还是加载缩小的图片到内存中。
-
预估加载整张图片到内存中所占用的大小
-
考虑加载图片到内存后当前应用还有多少内存可用
-
加载到UI对象中的尺寸
-
当前设备的屏幕尺寸及density
如何计算缩放比例
public static int calculateInSampleSize( BitmapFactory.Options options,int reqWidth,int reqHeight){ // Raw height and width of image finalint height = options.outHeight; finalint width = options.outWidth; int inSampleSize =1; if(height > reqHeight || width > reqWidth){ finalint halfHeight = height /2; finalint 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;}
计算出缩放比例后,加载图片到内存
public static Bitmap decodeSampledBitmapFromResource(Resources res,int resId, int reqWidth,int reqHeight){ // First decode with inJustDecodeBounds=true to check dimensions finalBitmapFactory.Options options =newBitmapFactory.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);}