android 内存溢出前退出界面,android内存溢出OutOfMemeory (OOM)

下面这些都是我在做各个app时总结出来的,希望对各位有用:

1.Android内存溢出原因

当图片过大,或图片数量较多时使用BitmapFactory解码图片会出java.lang.OutOfMemoryError: bitmap size exceeds VM budget,要想正常使用则需分配更少的内存

具体的解决办法是修改采样值BitmapFactory.Options.inSampleSize;设置此数值后会将图片的长宽缩放此int值后读入内存

2.恰当的inSampleSize

BitmapFactory.Options提供了另一个成员inJustDecodeBounds;设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。

Android提供了一种动态计算的方法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 the larger one when there is no overlapping zone.

return lowerBound;

}

if ((maxNumOfPixels == -1) &&

(minSideLength == -1)) {

return 1;

} else if (minSideLength == -1) {

return lowerBound;

} else {

return upperBound;

}

}

使用该算法,就可动态计算出图片的inSampleSize

BitmapFactory.Options opts = new BitmapFactory.Options();

opts.inJustDecodeBounds = true;

BitmapFactory.decodeFile(imageFile, opts);

opts.inSampleSize = computeSampleSize(opts, -1, 128*128);

opts.inJustDecodeBounds = false;

try {

Bitmap bmp = BitmapFactory.decodeFile(imageFile, opts);

imageView.setImageBitmap(bmp);

} catch (OutOfMemoryError err) {

}

3.使用ListView展示图片

大部分的app都是使用ListView来展示图片的,其实ListView的adapter是可以重用每个list的view的:

@Override

public View getView(int position, View convertView, ViewGroup parent) {

Log.e("ok", "position:"+position);

if (convertView == null) { // 这句就是重用的关键

convertView = LayoutInflater.from(context).inflate(R.layout.xxx, null);

}

}

重用convertView的方式也是google推荐的,这样系统是可以自动回收已经滑出屏幕的资源的。

但是重用convertView会有一个问题,就是如果图片是异步加载的,快速滑动listview时每个list的上的图片会不停的闪(以前做省电宝的时候遇到过这个bug),这个bug是由于我没有判断ImageView控件是否是重用的而引起的。 要解决这个bug,只需在异步加载图片的时候根据每个ImageView的tag判断一下ImageView是否重用:

boolean imageViewReused(ImageView iv, URL url) {

String tag=imageViewsHashMap.get(iv); // imageViewsHashMap是用来存储ImageView和tag的对应关系的(我是用图片的url来标识的tag,这个tag可以随意取值,只要能唯一的标识这个ImageView就好了)

if(tag==null || !tag.equals(url)) {

return true;

}

return false;

}

如果ImageView是重用的,就先设置一张默认的图片,等异步加载完成后再设置成最终要展示的图片。 如果ImageView不是重用的,忽略。

4.View隐藏或Activity退出时释放所有的Bitmap资源

之前在做壁纸的时候一直出现OOM的问题,找了好久都没有发现是怎么回事,之前做市场的时候也有遇到过,而且我确定我在使用了所有的Bitmap以后都手动释放了那些资源。这是一个很奇怪的事情,虽然我现在解了这个bug了,但是有些android的机制我还不是很理解,也希望有大拿指导一下。

下面是解决方案:

ImageView是存储在JVM里面的,但是Bitmap是存储在native heap里面的(3.0之前的系统是这样的),壁纸有一个大图浏览界面(一个Activity),每次进入这个界面的时候都会加载一张大图(大概在70K-200K),我加载大图的代码:

imageView.setImageBitmap(bitmap); //

我在每次Activity退出时都会主动的回收掉这个bitmap,代码:

if(bitmap != null && !bitmap.isrecycle) {

bitmap.recycle(); // 主动释放以后系统再GC也不管用,不知道为什么,我也尝试过对bitmap使用软引用/弱应用等,也都是不起作用

}

我在DDMS里面看到系统也会执行GC,但是就是不会回收我释放的这些资源,多次(一般3-5次)进入大图浏览界面以后一般就会报OOM的错误了。

后来我把退出时的代码改成了下面的形式:

imageView.setImageBitmap(null); // 不是主动的回收bitmap,而是主动的释放对bitmap的引用

imageView = null;

我从DDMS里面看到资源基本都被回收了,壁纸也基本不会再报OOM的错误了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值