什么是LowMemoryKiller 策略
在Android中,当运行的App被移动到后台之后,为了保证下次启动的速度,会将它移动到Cached的状态。这个时候,该App的进程依然存在,但组建可能已经被销毁(组件不占用内存)。这个时候用户再启动的速度就比较快,人称:热启动。如果该App的进程已经不存在了,重新启动又需要做很多初始化的工作,就会耗费多一些的时间,人称:冷启动。为了有更好的体验,当然尽可能提高App的存活几率。
LowMemoryKiller 策略就是指那些被退出到后台的App,并不是不会被清理掉的,指是可能没有持有任何组件,不占用CPU资源,少量占用内存空间。当系统认为内存空间不同时,会根据 LRU List 的列表来进行有先后顺序的清理工作,回收一些内存空间,供新启动的程序使用。
小知识:在adb中查看内存状况
- 查看系统整体内存使用状况
adb shell dumpsys meminfo
可以看到系统中运行的全部进程所占用的内存空间。
也能看到哪些App处于 Foreground状态,哪些处于Cached 状态。
如何进行内存优化
基于 LowMemoryKiller 策略,那么,为了让我们的App尽可能地存活下来,不被系统杀死。那就有两种方法:
- 提高进程优先级
- 降低内存占用
提高进程优先级,不在此文学习范围。我们就说说降低内存占用。
降低了内存占用,也就是相当于降低被回收的几率,但这是一个内存优化方案,并不能作为一个App后台保活的机制(因为即使内存优化的很低了,系统内存很小的时候,仍然会被回收,只是降低几率)。
怎么做?
如果在App中可以监听自己处于内存管理中的什么状态,我们就可以在适当的时机,做一些优化工作(比如在后台的时候回收一些需要显示的组件)。释放一些不需要持有的内存占用,来达到降低内存占用的目的。
onTrimMemory 方法
Android 4.0 之后 官方提供了一个 API,主要作用是提示开发者在系统内存不足时,通过释放一些不需要的内存资源,从而避免被系统杀掉。
onTrimMemory回调
在Android4.0之后,任何实现了 ComponentCallbacks2 接口的类都可以重写实现这个回调方法。
主要作用就是告知App当前处于系统内存回收的不同阶段的时机,在这些时机下进行自身的内存释放,以避免被系统直接杀掉,从而提高下次用户启动应用的速度,提高应用的用户体验。
onTrimMemory 会回调一个level参数,分别对应的含义是:
- TRIM_MEMORY_UI_HIDDEN
表示 App 目前所有的 UI 界面都被隐藏,最常见的就是 点击了 Home键 或者 Back键后的状态,App被移到后台不可见了,这时候应该释放一些UI资源。
这个等级比较常见
下面三个等级是当App正在运行时可能的回调参数:
- TRIM_MRMORY_RUNNING_MODERATE
表示 App目前正常运行,并且不会被杀掉,但是系统的可用内存已经有点低了,系统可能会开始根据 LRU list 来杀掉进程了。
- TRIM_MEMORY_RUNNING_LOW
表示 App 目前正常运行,并且不会被杀掉,但是系统的可用内存已经非常低了。
- TRIM_MEMORY_RUNNING_CRITCAL
表示 App 目前正常运行,但是系统已经开始根据 LUR list 杀掉大部分缓存的进程了。这个时候,我们应当尽可能地去释放任何不必要的资源,否则系统可能会继续杀掉缓存中的进程(有可能会杀掉一些本来应当保持运行的进程,比如后台运行的 service)
下面三个等级是当App正在处于Cached状态时可能的回调参数
- TRIM_MEMORY_BACKGROUND
表示 App目前处于后台,同时系统可用内存已经很低了,准备开始根据LRU list来清理内存,App目前在LRU list比较靠后的地方,暂时不会被杀掉。
- TRIM_MEMORY_MODERATE
表示 App目前处于后台,同时系统可用内存已经很低了,已经开始根据LRU list来清理内存, App目前在 LRU lits 的中间位置,如果系统可用内存仍然不够的话,App就有被系统杀掉的风险。
- TRIM_MEMORY_COMPLETE
表示 App目前处于后台,同时系统可用内存已经极低,并且处于 LRU list 靠前的位置,App随时都有可能被杀掉,应当尽可能地释放不必要的资源。
状态可以分类三类
- UI 置于后台: TRIM_MEMORY_UI_HIDDEN
- App正在前台运行状态:TRIM_MEMORY_RUNNING_...
- App正在后台状态,处于Cached状态:TRIM_MEMORY_...
这三类中,一般只需要关心当App处于Cached状态下的情况,因为系统是不会杀掉一个正在处于前台运行的App的(但有可能会有OOM的情况),但是有可能App的后台运行服务有被杀掉的风险。
当在Cached状态下时,当收到TRIM_MEMORY_... 时,就需要进行处理了,就算App处在靠后位置(COMPLETE),但是如果系统杀掉了LRU list前面的App后,内存仍然不够的话,也是会进一步杀掉更多的进程。也就是说,是有一种可能,会从 TRIM_MEMORY_BACKGROUND 瞬间变成 TRIM_MEMORY_COMPLETE 状态的。所以,App要尽可能完整地处理每一个状态,然后做好回收内存的工作。
哪些组件可以实现onTrimMemory回调
- Application
- Activity
- Fragment
- Service
- ContentProvider
onTrimMemory 回调中可以释放哪些资源?
我们在写App 的时候就以应该清楚,App哪些东西是需要常驻内存的,哪些东西是伴随界面生命周期而存在的。
一般来说,可以释放的资源有:
- 缓存:包括一些文件、图片缓存的,用户正常使用的时候这些缓存可以提高用户体验,但是当应用UI不存在的时候(又处在低内存的境地下),这些缓存就可以被清楚以期减少内存占用空间。比如:第三方图片库的缓存
- 一些动态生成动态添加的View:动态生成或者动态添加的View或者少数情况下才会用到的View,就可以被释放掉,下次使用的时候再进行动态生成或添加就可以了。
-
onTrimMemory 与 onStop 有什么区别?
既然需要释放资源,那我直接在 onStop的时候做不就好了?
onTrimMemory() 方法中的 TRIM_MEMORY_UI_HIDDEN 回调只有当App的所有UI组件全部不可见的时候才会触发,跟onStop() 还是有很大区别的。因为 onStop() 方法只是当一个 Activity 完全不可见时就会调用,比如说打开了App中的另一个Activity。
因此,我们可以在 onStop() 方法中去释放一些Activity相关的资源,比如 取消网络连接或者注销广播接收器等等,但是像UI相关的资源,建议是在 onTrimMemory(TRIM_MEMORY_UI_HIDDEN)回调时才去释放,这样才可以保证用户从后一个Activity返回前一个Activity时不需要重新加载界面资源,从而提升响应速度。
注意: onTrimMemory的 TRIM_MEMORY_UI_HIDDEN 等级是在 onStop 方法之前调用的。
为什么还有一个 onLowMemory
onTrimMemory() 是Android 4.0 以后才增加 的API。所以,为了兼容低版本的设备,可以监听 onLowMemory() 方法,它等同于 onTrimMemory(TRIM_MEMORY_COMPLETE) 的回调,也就是最紧急的情况了。
已发布至 我说的这句话是谎话