1.为什么显示图片的问题很棘手?
手机显示一张800万像素的图片(现在主流的手机像素都是在800万像素以上),大约需要32M的内存,而32M内存刚好是Android系统分配给应用的最大内存限制数目,当然有的手机分配是的16M,有的甚至只有8M,这个根据手机的总共RAM与版本有一定得联系,所以如果手机中的应用打开一张这样的图片的话,基本上都会遇到内存泄露的问题,导致的内存严重不足,一般情况下将会导致应用被强制退出
以Galaxy Nexus 为例,前后置的相机的像素是500W,其分辨率是2592*1936 如果位图使用的ARGB_8888(在Android 2.3以上版本一般默认设置是这种图片的格式) 加载该位图将大概占用19M 的内存(2592*1936*4 byte),因此程序会很快耗尽内存为每个应用分配的内存,从而导致程序员崩溃
即使应用程序不一定非得显示一张500W像素的图片,但是当在ListView,GridView中显示大量的图片,并且图片没有来得及回收的情况下,同事所占用的内存将是每张图片的总和,如果不对图片进行处理,将一样的会出现程序员因内存不足而强制退出
2.如何解决图片显示导致内存溢出的问题?
要解决图片显示导致内存的溢出,得考虑5个问题:
2.1如何高效的加载大位图
2.2如何在非UI线程中处理位图
2.3如何对位图进行缓存
2.4如何管理位图内存
2.5如何在Ui中显示位图
3.如何高效加载大位图
3.1获取位置以及尺寸
使用BitmaoFactory对位图进行解码的时候,使用BitmapFactory.Options ,将Options的injusDecodeBounds设为true时,可以避免为位图分配内存,此时的BitmapFactory.decdeX将返回null,但是会为Options设置outWidth,outHeight,outMemiType值
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;通过上述的代码你就可以在不为位图分配内存的情况下,获取位图的高与宽与位图的类型,之后在显示图片的时候,就可以通过获取的信息来判断是否需要对图片进行出来之后再显示,从未避免内存溢出的问题
3.2将缩小的图片加载到内存
如果仅仅需要对128*96的图片缩略图进行显示,而将一张原始大小为1024*768的图片加载到内存,就显得很不划算,因此对图片进行压缩之后再显示就很有必要了,因此对图片按照比例缩小再进行显示就显得很有必要了
通过设置Option.insampleSize来产生缩小之后的图片,例如Option.insampleSize=4,那么一张原始大小为2048*1536的位图来说,产生的新位图的大小约为512*384,将这么大的位图加载到内存却只需要0.75M 的内存,而原图片却需要大概12M的内存
具体程序实现如下,将injustDecodeBounds设置为ture获取到位图信息,然后再设置insampleSize的值,然后再将injustDecodeBounds设置为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); }
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) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // Choose the smallest ratio as inSampleSize value, this will guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; }
综上所述有了上述的方法,我们就可以在程序中,加载任意大小的图片,而不用担心内存溢出的问题,例如下面的代码将会把图片设置成100*100像素的缩略图:
mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));