背景:
这些天弄一个关于relativelayout中设置高斯模糊图片的事情,需求是从网络上获取到该背景图片.图片过大
做完后感觉还行,但是测试的时候发现背景多加载几次就会oom,通过as上的memory可以看到每次加载内存都会增加,但是却不会减少,通过查了一些资料发现以下一些结论:
在Android应用里,最耗费内存的就是图片资源。而且在Android系统中,读取位图Bitmap时,分给虚拟机中的图片的堆栈大小只有8M,如果超出了,就会出现OutOfMemory异常,但是在开发android的时候,很少会注意或者意识到释放内存的重要性,因为大家在使用过程中,涉及的图片资源不多,或者比较稳定,来回切换界面,图片也就那么几张或者使用的都是很小的图片,根本不会感觉到图片占用内存可能引发的潜在危机.
解决方法:
1,压缩图片
bitmap过大会产生oom,首先,这种方式是通过牺牲图片效果的方式的,所以压缩后可能效果没之前好了,但是我的需求本来就是需要高斯模糊,说白了就是模糊一点,所以图片效果并不是很重要,所以,压缩是可行的….方法是通过网络获取图片的流,代码如下
`new Thread(new Runnable() {
@Override
public void run() {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
//这里设置为true则不会在内存创建空间,即这个bitmap = null,这样就相当于获取了流中的数据却没有浪费内存
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new URL(background).openStream(), null, options);
//通过这两个方法可以得到图的宽高 这里重点是options,,这个是bitmap的压缩标准
int width = options.outWidth, height = options.outHeight;
int scale = 1;
int temp = width > height ? width : height;
//通过循环得到合适的压缩值 , scale的值得到后 , 压缩为之前的 1/scale
while (true) {
if (temp / 2 < 120)
break;
temp = temp / 2;
scale *= 2;
}
//我这里重新创建了一个opt,也可以不创建
BitmapFactory.Options opt = new BitmapFactory.Options();
//这里的inJustDecodeBounds不能为true,默认就是false,加入上面不创建的话这里需要options.inJustDecodeBounds = false 的声明,否则bitmap=null,
opt.inSampleSize = scale;
//这里的bitmap不为空了,并且已经是压缩之后的bitmap了 , 通过代码可以知道,宽高都不会大于240;
Bitmap bitmap = BitmapFactory.decodeStream(new URL(background).openStream(), null, opt);
blur(bitmap);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();`
2,及时回收bitmap的内存,来释放空间(1最重要 2没有1重要,但也非常必要)
Bitmap类有一个方法recycle(),从方法名可以看出意思是回收。这里就有疑问了,Android系统有自己的垃圾回收机制,可以不定期的回收掉不使用的内存空间,当然也包括Bitmap的空间。那为什么还需要这个方法呢?Bitmap类的构造方法都是私有的,所以开发者不能直接new出一个Bitmap对象,只能通过BitmapFactory类的各种静态方法来实例化一个Bitmap。仔细查看BitmapFactory的源代码可以看到,生成Bitmap对象最终都是通过JNI调用方式实现的。所以,加载Bitmap到内存里以后,是包含两部分内存区域的。简单的说,一部分是Java部分的,一部分是C部分的。这个Bitmap对象是由Java部分分配的,不用的时候系统就会自动回收了,但是那个对应的C可用的内存区域,虚拟机是不能直接回收的,这个只能调用底层的功能释放。所以需要调用recycle()方法来释放C部分的内存。从Bitmap类的源代码也可以看到,recycle()方法里也的确是调用了JNI方法了的。不管怎么说,释放空间还是用代码的,代码如下
`
//这里的意思是无论你是在xml中布局使用了android:background,还是在java代码中调用了setBackground( background );
//setBackgroundDrawable( background);setBackgroundResource(resid)等的方式去设置了背景图片.
//请在不需要的时候调用一下对应的方法
//setBackgroundResource和 android:background → setBackgroundResource(0);
//setBackgroundDrawable( background) → setBackgroundDrawable (null)
//setBackground ( background ) → setBackground (null)
//也就是说释放不了对象很多原因就是缺少这句代码的
mUserDetailBackground.setBackground(null);
//这是释放代码的方法
if (backgroundBitmap != null && !backgroundBitmap.isRecycled()) {
backgroundBitmap.recycle();
backgroundBitmap = null;
}
if (bitmapDrawable != null && !bitmapDrawable.getBitmap().isRecycled()) {
bitmapDrawable.getBitmap().recycle();
bitmapDrawable = null;
}
//最后在onDestoy中调用gc方法
@Override
protected void onDestroy() {
super.onDestroy();
System.gc();
}`
3,对程序要进行异常捕获
通过因为Bitmap是吃内存大户,为了避免应用在分配Bitmap内存的时候出现OutOfMemory异常以后Crash掉,需要特别注意实例化Bitmap部分的代码。通常,在实例化Bitmap的代码中,一定要对OutOfMemory异常进行捕获,所以我这里全部对异常进行了捕获,就是为了避免万一出问题而导致Crash,这段没代码,就是把代码try catch起来
4,缓存通用的Bitmap对象
这一段我没用到,但是网上看到的,感觉挺不错,就分享出来了.
有时候,可能需要在一个Activity里多次用到同一张图片。比如一个Activity会展示一些用户的头像列表,而如果用户没有设置头像的话,则会显示一个默认头像,而这个头像是位于应用程序本身的资源文件中的。如果有类似上面的场景,就可以对同一Bitmap进行缓存。如果不进行缓存,尽管看到的是同一张图片文件,但是使用BitmapFactory类的方法来实例化出来的Bitmap,是不同的Bitmap对象。缓存可以避免新建多个Bitmap对象,避免内存的浪费。