彻底解决Android因加载多个大图引起的OutOfMemoryError,内存溢出的问题
http://blog.csdn.net/wulianghuan/article/details/11548373?reload
最近因为项目里需求是选择或者拍摄多张照片后,提供滑动预览和上传,很多照片是好几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;
- }
- }
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));
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);
- }
- }
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张照片进行滑动预览:
希望以上所写对大家有帮助,谢谢!
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。