每个App所分配的最大内存在Android Compatibility Definition Document (CDD)中3.7节Virtual Machine Capability中有说明,最小至16MB。对Bitmap的处理不当,将很容易超出这个限制而导致OutOfMemoryException。
以下是官方文档推荐的一些处理Bitmap的优化措施。
Scale Bitmap before loading into memory
加载bitmap前的必要工作,使用BitmapFactory.Options 的 inJustDecodeBounds 选项加载按比例压缩后的bitmap到内存。
使用AsyncTask加载bitmap
这里需要注意在ListView,GridView这种组件中,当用户快速滑动时,处理不当将产生大量AsyncTask,并且AsyncTask由于完成的时机是不确定的,如何将UI上的Bitmap对应到AsyncTask上将是一个问题。
使用缓存机制
Memory cache
LruCache非常适合用来做Bitmap缓存。
Android 2.3之后,SoftReference不再适合在缓存中使用,因为2.3之后VM将更加激进的回收SoftReference对象,导致起不到缓存的效果。
Disk cache
使用磁盘缓存DiskLruCache来持久化memory cache。
重用bitmap内存
Android 3.0之前,bitmap的内存分配在native层,需要依靠app来显示调用recycle来释放它所占用的内存。在3.0之后,bitmap的内存分配在VM的堆上,因此bitmap的内存有GC来管理,一旦侦测到bitmap没有对象引用到它,GC会自动释放bitmap的内存。
随之而来的问题是,bitmap占用的空间比较大,GC在释放bitmap的内存时是一个昂贵的操作,如果你不断的创建新的bitmap对象,GC将不断的被触发起来工作,从而影响程序的性能。
Android3.0之后通过BitmapFactory.Options.inBitmap这个选项提供了重用bitmap内存这个功能。如果新的bitmap的大小小于一个已经存在的bitmap,那么我们可以重用这个bitmap对象,从而避免创建多个bitmap对象带来的昂贵的GC开销。
处理系统Configuration changed事件
系统configuration changed的时候由于Activity将默认被重启,如果加载Bitmap的AsyncTask或者线程没有被正确处理,可能导致产生许多无用的线程。