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

http://blog.csdn.net/wulianghuan/article/details/11548373?reload

 

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

 

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

 

 

  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.   
  44.     private static double getScaling(int src, int des) {  
  45.         /** 
  46.          * 48 目标尺寸÷原尺寸 sqrt开方,得出宽高百分比 49 
  47.          */  
  48.         double scale = Math.sqrt((double) des / (double) src);  
  49.         return scale;  
  50.     }  
  51.   
  52.     public static int computeSampleSize(BitmapFactory.Options options,  
  53.             int minSideLength, int maxNumOfPixels) {  
  54.         int initialSize = computeInitialSampleSize(options, minSideLength,  
  55.                 maxNumOfPixels);  
  56.   
  57.         int roundedSize;  
  58.         if (initialSize <= 8) {  
  59.             roundedSize = 1;  
  60.             while (roundedSize < initialSize) {  
  61.                 roundedSize <<= 1;  
  62.             }  
  63.         } else {  
  64.             roundedSize = (initialSize + 7) / 8 * 8;  
  65.         }  
  66.   
  67.         return roundedSize;  
  68.     }  
  69.   
  70.     private static int computeInitialSampleSize(BitmapFactory.Options options,  
  71.             int minSideLength, int maxNumOfPixels) {  
  72.         double w = options.outWidth;  
  73.         double h = options.outHeight;  
  74.   
  75.         int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math  
  76.                 .sqrt(w * h / maxNumOfPixels));  
  77.         int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(  
  78.                 Math.floor(w / minSideLength), Math.floor(h / minSideLength));  
  79.   
  80.         if (upperBound < lowerBound) {  
  81.             return lowerBound;  
  82.         }  
  83.   
  84.         if ((maxNumOfPixels == -1) && (minSideLength == -1)) {  
  85.             return 1;  
  86.         } else if (minSideLength == -1) {  
  87.             return lowerBound;  
  88.         } else {  
  89.             return upperBound;  
  90.         }  
  91.     }  
public static byte[] decodeBitmap(String path) {
		BitmapFactory.Options opts = new BitmapFactory.Options();
		opts.inJustDecodeBounds = true;// 设置成了true,不占用内存,只获取bitmap宽高
		BitmapFactory.decodeFile(path, opts);
		opts.inSampleSize = computeSampleSize(opts, -1, 1024 * 800);
		opts.inJustDecodeBounds = false;// 这里一定要将其设置回false,因为之前我们将其设置成了true
		opts.inPurgeable = true;
		opts.inInputShareable = true;
		opts.inDither = false;
		opts.inPurgeable = true;
		opts.inTempStorage = new byte[16 * 1024];
		FileInputStream is = null;
		Bitmap bmp = null;
		ByteArrayOutputStream baos = null;
		try {
			is = new FileInputStream(path);
			bmp = BitmapFactory.decodeFileDescriptor(is.getFD(), null, opts);
			double scale = getScaling(opts.outWidth * opts.outHeight,
					1024 * 600);
			Bitmap bmp2 = Bitmap.createScaledBitmap(bmp,
					(int) (opts.outWidth * scale),
					(int) (opts.outHeight * scale), true);
			bmp.recycle();
			baos = new ByteArrayOutputStream();
			bmp2.compress(Bitmap.CompressFormat.JPEG, 100, baos);
			bmp2.recycle();
			return baos.toByteArray();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
				baos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			System.gc();
		}
		return baos.toByteArray();
	}

	private static double getScaling(int src, int des) {
		/**
		 * 48 目标尺寸÷原尺寸 sqrt开方,得出宽高百分比 49
		 */
		double scale = Math.sqrt((double) des / (double) src);
		return scale;
	}

	public static int computeSampleSize(BitmapFactory.Options options,
			int minSideLength, int maxNumOfPixels) {
		int initialSize = computeInitialSampleSize(options, minSideLength,
				maxNumOfPixels);

		int roundedSize;
		if (initialSize <= 8) {
			roundedSize = 1;
			while (roundedSize < initialSize) {
				roundedSize <<= 1;
			}
		} else {
			roundedSize = (initialSize + 7) / 8 * 8;
		}

		return roundedSize;
	}

	private static int computeInitialSampleSize(BitmapFactory.Options options,
			int minSideLength, int maxNumOfPixels) {
		double w = options.outWidth;
		double h = options.outHeight;

		int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math
				.sqrt(w * h / maxNumOfPixels));
		int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(
				Math.floor(w / minSideLength), Math.floor(h / minSideLength));

		if (upperBound < lowerBound) {
			return lowerBound;
		}

		if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
			return 1;
		} else if (minSideLength == -1) {
			return lowerBound;
		} else {
			return upperBound;
		}
	}



 

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

 

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

 

 


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

 

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

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.ImageView;

public class AsyncImageLoaderByPath {
    //SoftReference是软引用,是为了更好的为了系统回收变量
    private HashMap<String, SoftReference<Bitmap>> imageCache;
    private Context context;
    
    public AsyncImageLoaderByPath(Context context) {
        this.imageCache = new HashMap<String, SoftReference<Bitmap>>();
        this.context = context;
    }
    public Bitmap loadBitmapByPath(final String imagePath, final ImageView imageView, final ImageCallback imageCallback){
        if (imageCache.containsKey(imagePath)) {
            //从缓存中获取
            SoftReference<Bitmap> softReference = imageCache.get(imagePath);
            Bitmap bitmap = softReference.get();
            if (bitmap != null) {
                return bitmap;
            }
        }
        final Handler handler = new Handler() {
            public void handleMessage(Message message) {
                imageCallback.imageLoaded((Bitmap) message.obj, imageView, imagePath);
            }
        };
        //建立新一个获取SD卡的图片
        new Thread() {
            @Override
            public void run() {
            	Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);
                imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));
                Message message = handler.obtainMessage(0, bitmap);
                handler.sendMessage(message);
            }
        }.start();
        return null;
    }
    //回调接口
    public interface ImageCallback {
        public void imageLoaded(Bitmap imageBitmap,ImageView imageView, String imagePath);
    }
}



 

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

以下是运行效果

SD卡中的图片:


 

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



 


 

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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。