您的位置:首页 > 移动开发 > Android开发

解决Android因加载多个大图引起的OutOfMemoryError

2014-12-10 16:34 267 查看
转载自:/article/1819572.html

最近因为项目里需求是选择或者拍摄多张照片后,提供滑动预览和上传,很多照片是好几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的错误了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐