Android 进程保活

Android 进程特性

Android 进程优先级

在 Android 系统中,当内存不足时,为了新建进程或运行更重要的进程,会根据进程的优先级选择杀掉不那么重要的进程。进程的优先级如下所示:

前台进程

  • 与用户正在交互的 Activity(即已调用 ActivityonResume() 方法)
  • 正在执行 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
2
3
4
5
6
7
for(i = 0; i < array_size; i++) {
if (other_free < lowmem_minfree[i] &&
other_file < lowmem_minfree[i]) {
min_adj = lowmem_adj[i];
break;
}
}
  • 遍历所有进程找到 oom_adj > min_adj 并且占用内存大的进程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for_each_process(p) {
if (p->oomkilladj < min_adj || !p->mm)
continue;
tasksize = get_mm_rss(p->mm);
if (tasksize <= 0)
continue;
if (selected) {
if (p->oomkilladj < selected->oomkilladj)
continue;
if (p->oomkilladj == selected->oomkilladj &&
tasksize <= selected_tasksize)
continue;
}
selected = p;
selected_tasksize = tasksize;
}
  • 发送 Signal 信号 kill 掉

进程优先级提升策略

设置 persistent 属性

1
2
3
<application android:name="App"
android:persistent="true"
android:icon="@drawable/ic_launcher">

这种方式只对系统应用有用,即存放在 /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 进程会杀掉。