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

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


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


 

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():

 

Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);

                imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));

 



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

 

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卡中的图片:

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


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

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

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


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


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


 

你可能感兴趣的:(彻底解决Android因加载多个大图引起的OutOfMemoryError,内存溢出的问题)