本文主要记录日常开发中关于内存管理的使用建议,便于查找。大部分来自郭霖的博客,转载地址http://blog.csdn.net/guolin_blog/article/details/42238627。
谨慎使用Service
在使用Service来执行后台任务时,尽量在需要时才启动Service;且在任务完成后,主动调用stopService或unbindService停止任务。注意,需要Service停止失败导致内存泄漏的问题。
当启用Service时,安卓系统会倾向于尽量保留Service所在进程,耗费系统内存。##Android官方推荐使用IntentService,该组件在后台任务执行结束后可以自动停止##,尽量避免了内存泄漏的可能。
使用优化过的数据集合
Android API当中提供了一些优化过后的数据集合工具类,如SparseArray,SparseBooleanArray,以及LongSparseArray等,使用这些API可以让我们的程序更加高效。传统Java API中提供的HashMap工具类会相对比较低效,因为它需要为每一个键值对都提供一个对象入口,而SparseArray就避免掉了基本数据类型转换成对象数据类型的时间。
Bitmap使用
Bitmap对象占用的内存空间与像素有直接的关系,且与图片在硬盘中的大小无关,建议不要加载不需要的分辨率。以一张100k的图片为例,像素是15001000,使用ARGB_8888的颜色类型,则每个像素点会占用4个字节的内存,占用的总内存是15001000*4字节,即5.7M。
BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。比如SD卡中的图片可以使用decodeFile方法,网络上的图片可以使用decodeStream方法,资源文件中的图片可以使用decodeResource方法。这些方法会尝试为已经构建的bitmap分配内存,如果内存不足就会出现OOM。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。
当加载多图时,可以考虑使用内存缓存LruCache来快速访问图片。该类在android-support-v4包中提供,主要的原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
目前不建议使用软引用或弱引用 (SoftReference or WeakReference)来实现内存缓存,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃。
还有一种磁盘缓存DiskLruCache可用来实现图片的磁盘缓存,解决内存缓存空间有限的问题。DiskLruCache是非Google官方编写,但获得官方认证的硬盘缓存类,后续需要学习该类的原理和使用方法。
内存的开支情况
我们还应当清楚我们所使用语言的内存开支和消耗情况,并且在整个软件的设计和开发当中都应该将这些信息考虑在内。可能有一些看起来无关痛痒的写法,结果却会导致很大一部分的内存开支,例如:
- 使用枚举通常会比使用静态常量要消耗两倍以上的内存,在Android开发当中我们应当尽可能地不使用枚举。
- 任何一个Java类,包括内部类、匿名类,都要占用大概500字节的内存空间。
- 任何一个类的实例要消耗12-16字节的内存开支,因此频繁创建实例也是会一定程序上影响内存的。eg不要在onDraw中频繁创建对象
- 在使用HashMap时,即使你只设置了一个基本数据类型的键,比如说int,但是也会按照对象的大小来分配内存,大概是32字节,而不是4字节。因此最好的办法就是像上面所说的一样,使用优化过的数据集合。
避免使用依赖注入框架
这类框架一般使用注解实现,可以省略findViewById()这一类的繁琐操作,简化一些编码。但是这些框架为了要搜寻代码中的注解,通常都需要经历较长的初始化过程,并且还可能将一些你用不到的对象也一并加载到内存当中。这些用不到的对象会一直占用着内存空间,可能要过很久之后才会得到释放,相较之下,也许多敲几行看似繁琐的代码才是更好的选择。
使用ProGuard简化代码
ProGuard相信大家都不会陌生,很多人都会使用这个工具来混淆代码,但是除了混淆之外,它还具有压缩和优化代码的功能。ProGuard会对我们的代码进行检索,删除一些无用的代码,并且会对类、字段、方法等进行重命名,重命名之后的类、字段和方法名都会比原来简短很多,这样的话也就对内存的占用变得更少了。