彻底解决Android因加载多个大图引起的OutOfMemoryError,内存溢出的问题

最近因为项目里需求是选择或者拍摄多张照片后,提供滑动预览和上传,很多照片是好几MB一张,因为目前的Android系统对运行的程序都有一定的内存限制,一般是16MB或24MB(视平台而定),不做处理直接加载的话必然会报OOM (Out Of Memmory)。网上有很多解决android加载bitmap内存溢出的方法,我总结了一个通用的方法,下面是我从的开发案例抽取出来的代码:

 

我在项目中建了个Util.java工具类,里面写了个方法,根据图片的路径返回一个字节流数组对象:

 

 

[java][/java] view plaincopy

  1. public static byte[] decodeBitmap(String path) {
  2.         BitmapFactory.Options opts = new BitmapFactory.Options();
  3.         opts.inJustDecodeBounds = true;// 设置成了true,不占用内存,只获取bitmap宽高
  4.         BitmapFactory.decodeFile(path, opts);
  5.         opts.inSampleSize = computeSampleSize(opts, -1, 1024 * 800);
  6.         opts.inJustDecodeBounds = false;// 这里一定要将其设置回false,因为之前我们将其设置成了true
  7.         opts.inPurgeable = true;
  8.         opts.inInputShareable = true;
  9.         opts.inDither = false;
  10.         opts.inPurgeable = true;
  11.         opts.inTempStorage = new byte[16 * 1024];
  12.         FileInputStream is = null;
  13.         Bitmap bmp = null;
  14.         ByteArrayOutputStream baos = null;
  15.         try {
  16.             is = new FileInputStream(path);
  17.             bmp = BitmapFactory.decodeFileDescriptor(is.getFD(), null, opts);
  18.             double scale = getScaling(opts.outWidth * opts.outHeight,
  19.                     1024 * 600);
  20.             Bitmap bmp2 = Bitmap.createScaledBitmap(bmp,
  21.                     (int) (opts.outWidth * scale),
  22.                     (int) (opts.outHeight * scale), true);
  23.             bmp.recycle();
  24.             baos = new ByteArrayOutputStream();
  25.             bmp2.compress(Bitmap.CompressFormat.JPEG, 100, baos);
  26.             bmp2.recycle();
  27.             return baos.toByteArray();
  28.         } catch (FileNotFoundException e) {
  29.             e.printStackTrace();
  30.         } catch (IOException e) {
  31.             e.printStackTrace();
  32.         } finally {
  33.             try {
  34.                 is.close();
  35.                 baos.close();
  36.             } catch (IOException e) {
  37.                 e.printStackTrace();
  38.             }
  39.             System.gc();
  40.         }
  41.         return baos.toByteArray();
  42.     }
  43.     private static double getScaling(int src, int des) {
  44.         /**
  45.          * 48 目标尺寸÷原尺寸 sqrt开方,得出宽高百分比 49
  46.          */
  47.         double scale = Math.sqrt((double) des / (double) src);
  48.         return scale;
  49.     }
  50.     public static int computeSampleSize(BitmapFactory.Options options,
  51.             int minSideLength, int maxNumOfPixels) {
  52.         int initialSize = computeInitialSampleSize(options, minSideLength,
  53.                 maxNumOfPixels);
  54.         int roundedSize;
  55.         if (initialSize <= 8) {
  56.             roundedSize = 1;
  57.             while (roundedSize < initialSize) {
  58.                 roundedSize <<= 1;
  59.             }
  60.         } else {
  61.             roundedSize = (initialSize + 7) / 8 * 8;
  62.         }
  63.         return roundedSize;
  64.     }
  65.     private static int computeInitialSampleSize(BitmapFactory.Options options,
  66.             int minSideLength, int maxNumOfPixels) {
  67.         double w = options.outWidth;
  68.         double h = options.outHeight;
  69.         int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math
  70.                 .sqrt(w * h / maxNumOfPixels));
  71.         int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(
  72.                 Math.floor(w / minSideLength), Math.floor(h / minSideLength));
  73.         if (upperBound < lowerBound) {
  74.             return lowerBound;
  75.         }
  76.         if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
  77.             return 1;
  78.         } else if (minSideLength == -1) {
  79.             return lowerBound;
  80.         } else {
  81.             return upperBound;
  82.         }
  83.     }

 

然后在我需要加载图片BitMap的地方来调用Util.decodeBitmap():

 

[java][/java] view plaincopy

  1. Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);
  2.                 imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));

 

 
上面这两行是我的AsyncImageLoaderByPath类中的代码,用来加载SD卡里面的图片,该类完整代码是:

 

[java][/java] view plaincopy

  1. package com.pioneer.travel.util;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.lang.ref.SoftReference;
  5. import java.net.MalformedURLException;
  6. import java.net.URL;
  7. import java.util.HashMap;
  8. import android.content.Context;
  9. import android.graphics.Bitmap;
  10. import android.graphics.BitmapFactory;
  11. import android.graphics.BitmapFactory.Options;
  12. import android.graphics.drawable.Drawable;
  13. import android.os.Handler;
  14. import android.os.Message;
  15. import android.provider.MediaStore;
  16. import android.util.Log;
  17. import android.widget.ImageView;
  18. public class AsyncImageLoaderByPath {
  19.     //SoftReference是软引用,是为了更好的为了系统回收变量
  20.     private HashMap<String, SoftReference<Bitmap>> imageCache;
  21.     private Context context;
  22.     public AsyncImageLoaderByPath(Context context) {
  23.         this.imageCache = new HashMap<String, SoftReference<Bitmap>>();
  24.         this.context = context;
  25.     }
  26.     public Bitmap loadBitmapByPath(final String imagePath, final ImageView imageView, final ImageCallback imageCallback){
  27.         if (imageCache.containsKey(imagePath)) {
  28.             //从缓存中获取
  29.             SoftReference<Bitmap> softReference = imageCache.get(imagePath);
  30.             Bitmap bitmap = softReference.get();
  31.             if (bitmap != null) {
  32.                 return bitmap;
  33.             }
  34.         }
  35.         final Handler handler = new Handler() {
  36.             public void handleMessage(Message message) {
  37.                 imageCallback.imageLoaded((Bitmap) message.obj, imageView, imagePath);
  38.             }
  39.         };
  40.         //建立新一个获取SD卡的图片
  41.         new Thread() {
  42.             @Override
  43.             public void run() {
  44.                 Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);
  45.                 imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));
  46.                 Message message = handler.obtainMessage(0, bitmap);
  47.                 handler.sendMessage(message);
  48.             }
  49.         }.start();
  50.         return null;
  51.     }
  52.     //回调接口
  53.     public interface ImageCallback {
  54.         public void imageLoaded(Bitmap imageBitmap,ImageView imageView, String imagePath);
  55.     }
  56. }

 

通过这个实例,我验证了一下,一次性获取20张5MB的照片,都可以加载的很流畅,完全没有再出现报OOM的错误了

以下是运行效果

SD卡中的图片:

 

进入应用,选择11张照片进行滑动预览:

 

 

希望以上所写对大家有帮助,谢谢!

标签