与图片有关的软件免不了要做Bitmap的管理,要不然很可能就会发生OutOfMemory(OOM)的错误,致使程序崩溃。引起这种现象的原因无非是:
1.长期保持对某个对象或数据的引用,导致java的垃圾回收器不能回收不再使用的内存空间;
2.正在使用的有效对象占用内存空间大,导致多个有效对象生成时,吃完程序所分配的内存;
一般会报这种错误:java.lang.OutOfMemoryError: bitmap size exceeds VM budget,这是因为,android系统中读取位图Bitmap时.分给虚拟机中图片的堆栈大小只有8M。所以不管是如何调用的图片,太多太大虚拟机肯定会报这个错误。(对于实际的机器来说,这个内存分配是不是很大?)
而像相册这样子的应用,最好的效果是:
1)将图片(单个资源)缩小到适当的大小,以便于同时加载多张图片;
2)就算缩小到适当的大小,我们也不能将所有图片的引用同时保存在内存中,这时候需要将部分的图片释放;
遇到这种问题的解决方案是:缩小图片+回收资源的方式,来优化内存:
尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。如果在读取时加上图片的Config参数,可以更有效减少加载的内存,从而更有效阻止抛out of Memory异常。
另外,decodeStream直接拿的图片来读取字节码了, 不会根据机器的各种分辨率来自动适应,使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相应的图片资源,否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了。
1)缩小图片的方式:
InputStream is = this.getResources().openRawResource(R.drawable.pic1); BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds = false; options.inSampleSize = 10; //width,hight设为原来的十分一 Bitmap btp =BitmapFactory.decodeStream(is,null,options);
2)回收图片的方式:
if(!bmp.isRecycle() ){ bmp.recycle() //回收图片所占的内存 system.gc() //提醒系统及时回收 }
缓存:加载图片的时候首先检测是否被缓存然后再去动作
//用来存放图片的缓存 HashMap<Integer, Bitmap> bitmapCache = new HashMap<Integer, Bitmap>(); //如果没有图片,或者已经存在 if(bitmapCache.isEmpty() || !AppConst.bitmapCache.containsKey(position)){ bitmapCache.put(position, bm); System.out.println("-----------inset cache---------"); }
定义FreeBitmap函数,在activity结束的时候,调用FreeBitmap函数,回收map中的资源
private void FreeBitmap(HashMap<Integer, Bitmap> cache){ if(cache.isEmpty()){ return; } for(Bitmap bitmap:cache.values()){ if(bitmap != null && !bitmap.isRecycled()){ bitmap.recycle(); System.out.println("=============recycle bitmap======="); } } cache.clear(); }