Android 进程特性
Android 进程优先级
在 Android 系统中,当内存不足时,为了新建进程或运行更重要的进程,会根据进程的优先级选择杀掉不那么重要的进程。进程的优先级如下所示:
前台进程
- 与用户正在交互的
Activity
(即已调用Activity
的onResume()
方法) - 正在执行
onReceive()
方法的BroadcastReceiver
- 正在执行
onCreate()
、onStart()
或onDestroy()
等生命周期回调的Service
- 与前台 Activity 绑定的
Service
,或正在前台运行的Service
(服务已调用startForeground()
)
系统中的前台进程并不会很多,只有在内存不足以支持它们同时继续运行时,系统才会终止它们。
可见进程
- 处于前台、但仍对用户可见的
Activity
(已调用其onPause()
方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity 的情况。 - 与可见 Activity 绑定的
Service
。
可见进程被一般不会被系统杀死,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。
服务进程
启动的 service 且不属于上述两个更高级别进程。除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。
后台进程
- 不可见的 Activity 的进程(已调用 Activity 的
onStop()
方法)。
不影响用户的体验,为了维持前台进程、可见进程或服务进程的运行,系统可能随时终止它们。当不可见的 Activity 被终止后,系统会保存当前的状态,当用户导航回到该 Activity 时,会恢复之前运行时的状态。
空进程
- 不包含任何活动应用组件的进程。
系统会优先终止空进程,它一般用作缓存,以缩短下次运行组件的启动时间。
Android 进程回收策略
Linux 系统采用的是 OOMkiller(Out Of Memory Killer) 机制进行进程内存回收,系统无法分配新内存时,选择杀掉内存占用最多的进程。这种机制需要等到 OOM 时才杀进程,但 OOM 时系统已经处于异常状态了。
Android 系统使用 LowMemoryKiller 机制,它会每隔一段时间检查一次,当系统剩余内存较低时,根据不同的剩余内存档位来选择选择杀掉低优先级且占用大的进程。
oom_adj 代表进程的优先级,数值越大,优先级越低,就越容易被杀,其值如下:
ADJ级别 | 取值 | 描述 |
---|---|---|
UNKNOWN_ADJ | 16 | 一般指将要会缓存进程,无法获取确定值 |
CACHED_APP_MAX_ADJ | 15 | 缓存进程,空进程,在内存不足的情况下就会优先被kill |
CACHED_APP_MIN_ADJ | 9 | 缓存进程,也就是空进程 |
SERVICE_B_AD | 8 | 不活跃的进程 |
PREVIOUS_APP_ADJ | 7 | 上一个App的进程(往往通过按返回键) |
HOME_APP_ADJ | 6 | 与Home交互的进程 |
SERVICE_ADJ | 5 | 服务进程(Service process) |
HEAVY_WEIGHT_APP_ADJ | 4 | 高权重进程 |
BACKUP_APP_ADJ | 3 | 备份进程 |
PERCEPTIBLE_APP_ADJ | 2 | 可感知进程,比如后台音乐播放 |
VISIBLE_APP_ADJ | 1 | 可见进程(Visible process) |
FOREGROUND_APP_ADJ | 0 | 前台进程(Foreground process) |
PERSISTENT_SERVICE_ADJ | -11 | 关联着系统或persistent进程 |
PERSISTENT_PROC_ADJ | -12 | 系统persistent进程,比如telephony |
SYSTEM_ADJ | -16 | 系统进程 |
NATIVE_ADJ | -17 | native进程(不被系统管理) |
Android 杀进程逻辑
- 获取当前剩余内存,根据内存阈值表比较获得阈值内存对应的 min_adj
1 | for(i = 0; i < array_size; i++) { |
- 遍历所有进程找到 oom_adj > min_adj 并且占用内存大的进程
1 | for_each_process(p) { |
- 发送 Signal 信号 kill 掉
进程优先级提升策略
设置 persistent 属性
1 | <application android:name="App" |
这种方式只对系统应用有用,即存放在 /system/priv-app
和 /system/app
目录下的应用。
多进程
根据进程的回收机制,系统会优先回收占用内存高的优先级低的进程,拆分进程可降低 push 进程的内存占用,尽量减少 push 进程被杀的可能。
前台服务
Service 进程优先级为5,通过 setForeground
接口可以将 Service 设置为前台 Service。
- 对于 API level < 18 :调用
startForeground(ID, new Notification())
,发送空的Notification ,图标则不会显示。 - 对于 API level >= 18:在需要提优先级的 service A 启动一个 InnerService,两个服务同时
startForeground
,且绑定同样的 ID。Stop 掉 InnerService ,这样通知栏图标即被移除。
一个像素 Activity
监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的透明 Activity,在用户解锁时将 Activity 销毁掉,通过该方案可使进程优先级提升为最高优先级0。
进程死后拉活策略
Service自启动机制
原理:原生支持,service 设置为START_STICKY
,挂掉之后系统会重启该 service。
缺陷:
- 短时间内 Service 被杀过多则系统不再拉起
- 通过
forcestop
停止掉,无法重启。 - 受系统权限管理
静态广播
原理:静态广播在进程没有运行的时候,也可以收到,这时候会先启动进程,然后处理广播onReceive
函数。
缺陷:
- 受系统权限管理影响
- 系统广播事件不可控
账号同步机制
原理:可以设置定期同步账号,同步时系统会启动对应的 Service,目前适用于7.0之前所有版本,包括被 forestop
掉的进程也能进行拉活。
缺陷:
- Android N 中系统对账户同步这里做了变动,该方法不再有效
- 使用不当容易成为耗电大户
JobScheduler(5.0+)
原理:系统在 Android 5.0 以上版本提供了 JobScheduler
接口,系统会定期调用该进程以使应用进行一些逻辑操作。
缺陷:
- 只适用于 Android 5.0 以上版本手机,某些厂商 Rom 也做了限制
Native 守护进程
原理:Native进程不受 AMS 管理(<5.0),利用 Linux 的 fork 机制创建 Native 进程,通过死循环或定时器轮询判断主进程是否存活。
缺陷:
- 只适用于 Android 5.0 以下版本的手机,在 Android 5.0 以上,系统会依次将所有的 native 进程会杀掉。