Android进程保活

4,850 阅读10分钟

一、前言

Android系统进程保活主要包括两个层面: 1、提高进程的优先级,从而降低进程被杀死的概率。 2、在进程被杀死后,对进程进行拉活。

我们先来看一下进程的优先级: Android系统将尽量保持应用进程,但是为了新建进程或者运行更重要的进程,最终需要清除旧进程来回收内存。 为了确定保留或者终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。在对进程进行终止时,首先会清除一些重要性低一点的进程,以此类推,以回收系统资源。

根据进程的优先级,可以将进行划分为5级: 1、前台进程(Foreground process) 2、可见进程(Visible process) 3、服务进程(Service process) 4、后台进程(Background process) 5、空进程(Empty process) 下面分别来阐述这五种进程的特点: 1、前台进程(Foreground process) 用户当前操作所必需的进程。在给定时间内,前台进程同时存在的数量并不多。只有在内存不足以支持它们同时存在时,系统才会终止它们。 前台进程的一些例子: A、拥有用户正在交互的Activity(已调用onResume) B、拥有某个Service,该Service和用户正在交互的Activity绑定在一起。 C、拥有正在“前台”运行的Service(服务已调用startForeground) D、拥有正执行一个生命周期回调的Service(onCreate、onStartCommand或者onDestroy) E、拥有正在执行器onReceive方法的BroadcastReceiver

2、可见进程(Visible process) 没有任何前台组件,但仍会影响用户在屏幕上所见内容的进程。可见进程被视为极其重要的进程,除非为了维持所有前台进程同时运行而必须终止可见进程,否则是不会终止这些进程的。 可见进程的一些例子: A、拥有不在前台,但仍对用户可见的Activity(已调用onPause) B、拥有绑定到可见(或者前台)Activity的Service

3、服务进程(Service process) 服务进程与用户所见内容没有直接的联系,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。 服务进程的一些例子: A、使用startService启动的服务,并且不属于上述两个更高级别的进程。

4、后台进程(Background process) 后台进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存给前台进程、可见进程和服务进程。通常会有很多后台进程在运行,因此它们会出现在LRU列表中,以确保包含用户最近查看的Activity的进程最后一个被终止。如果某个Activity正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显的影响,因为当用导航回该Activity时,Activity会恢复所有可见状态。 后台进程的一些例子: A、对用户不可见的Activity的进程(已调用Activity的onStop方法)

5、空进程(Empty process) 保留这种进程的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。为了总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。 空进程的一些例子: A、不含任何活动的应用组件的进程。

具体可以参考官方文档的介绍:developer.android.com/intl/zh-cn/…](developer.android.com/intl/zh-cn/…

二、Android进程回收策略

Android中内存的回收,主要是由Lowmemorykiller来完成的,它是一种根据OOM_ADJ阈值级别触发相应力度的内存回收机制。

一般来说,OOM_ADJ为0代表的是前台进程,大于0的表示一些后台进程或者是服务进程。而OOM_ADJ小于0表示非Android进程(纯Linux进程)。 在Lowmemorykiller回收内存时会根据进程的级别优先杀死OOM_ADJ比较大的进程,而对于优先级相同的进程则优先杀死那些占用内存和存活时间比较大的进程。

怎么查看进程的优先级(oom_adj的值)? 1、在命令行模式下,使用adb shell命令进入shell模式。 2、ps命令可以列举出所有正在运行的进程的信息。当然我们可以使用ps | grep 进程名(一般为包名)来过滤得到我们想要的进程。 3、从第二步获取进程的id,然后使用 cat /proc/进程id/oom_adj获取该进程的优先级。 具体做法可以参考下面的例子:

查看进程的优先级

PS:查看进程优先级时,在第三步时会返回permission denied。那是因为没有获得手机的root权限,所以上述操作要在已root的手机或者模拟器上进行。

综上所述,为了减少进程被杀的概率,则要想办法提高进程的优先级。接下来我们将从进程优先级的提升方面讲一下几种可行方案。

三、提升进程优先级的方案

1、利用Activity提升进程优先级

1.1 方案设计: 监控手机的锁屏和解锁事件,在屏幕锁屏时启动1个像素的Activity,在解锁时将Activity销毁。该Activity要设计成用户无感知。 这里有个前提是APP进程需要在后台。如果APP进程在前台,那么进程已经是前台进程了,那么上面的做法就没有任何意义了。

通过该方案,进程的优先级可以在锁屏时由oom_adj为9(不同手机系统的值可能不一样)提升到0(前台进程)。这样我们的APP就不容易在锁屏时间内被第三方应用给杀死。

1.2 适用范围: 场景:该方案主要解决的痛点是第三方应用或者系统管理工具会在检测到锁屏事件一段时间后杀死后台进程,以达到省电的目的。

版本:适用于所有的Android版本。

1.3 具体实现: 可参考Github上的开源项目KeepProcessLive。 对于该开源项目,目前有一个疑问,在监听手机的锁屏解锁事件时,会多次监听到锁屏、解锁操作,导致有时候该方案无效。

2、利用Notification提升进程优先级

2.1 方案设计: 通过将Android中的Service的setForeground接口可以将后台Service设置为前台Service,这样进程的优先级就被提升到了2,从而使进程的优先级仅仅次于用户当前正在交互的进程,与可见进程的优先级一致,使进程被杀死的概率大大降低。

在Android 2.3开始调用setForeground将后台Service设置为前台Service时,必须在系统的通知栏发送一条通知,这意味着前台Service与一条可见的通知是绑定在一起的。但是对于不需要使用常驻通知栏的应用来说,该方案是用户感知的,没有办法直接使用。

我们可以通过实现一个内部Service,在LiveService和其内部Service中同时发送具有相同ID的Notification,然后将内部Service结束掉,同时也将Notification结束掉,随着Notification的消失,用户就无感知了,同时也实现了将系统优先级提升到2的操作。

2.2 适用范围: 版本:适用于所有的Android版本。

2.3 具体实现: 可参考Github上的开源项目KeepProcessLive

四、进程被杀后的拉活方案

1、利用系统广播拉活

1.1 方案设计: 在发生特定系统事件时,系统会发出响应的广播,如果我们在AndroidManifest中“静态”注册对应的广播监听器,就可以在发生响应事件时进行拉活。 常用的用于拉活的广播事件包括:

1.2 适用范围: 适用于全部的Android平台。但是存在着以下几个缺点: 1、广播接收器被管理软件或者系统软件通过“自启动”等功能禁用的场景无法接收到广播,从而无法自启。 2、系统广播事件是不可控制的,只能在保证发生广播事件时拉活进程,但是无法做到进程挂掉后立即拉活。

因此,该方案只是作为一个备选方案。

2、利用第三方应用广播拉活

2.1 方案设计: 该方案的设计思想和接收系统广播的类似,不同的是该方案接收的是第三方Top应用的广播。

通过反编译第三方Top应用,如:手机QQ、微信、支付宝、UC浏览器等,以及友盟、信鸽、个推等SDK,找出它们外发的广播,在应用中进行监听,当这些应用发出广播时,就会将我们的应用进行拉活。

2.2 适用范围: 该方案的缺点和上面所说的系统广播的方案一样之外,还有下面几个缺点: 1、进程拉活的效果取决于反编译分析过的第三方应用的多少。 2、第三方应用的广播属于应用私有的,所以有可能存在当前版本有效的广播在后续的版本中随时有可能被移除或者被改为不外发。

因此,该方案也只是作为一个备选方案。

3、利用系统Service机制拉活

3.1 方案设计: 该方案将Service的onStartCommand方法的返回值设置为START_STICKY,利用系统机制在Service挂掉后自动拉活。

3.2 适用范围: 下面这两种情况无法进行拉活: 1、Service在第一次被异常杀死后会在5秒内重启,第二次被杀死后会在10秒重启,第三次杀死后会在20秒重启,但是一旦在短时间内Service被杀死的次数达到5次,则系统不再拉起。 2、进程被取得Root权限的管理工具或系统工具通过foreStop停止掉,则无法重启。

4、利用JobScheduler机制拉活

4.1 方案设计: Android 5.0以后的版本提供了JobScheduler接口,系统会定时调用该进程以使应用进行一些逻辑操作。

4.2 适用范围: 主要适用于Android 5.0以上的版本。在Android 5.0以上版本中不受forcestop影响,被强制停止的应用依然可以被拉起,在Android 5.0以上版本拉活效果是非常好的。(但是在小米手机上可能会偶尔出现无法拉活的问题。)

4.3 具体实现: 可参考Github上的开源项目KeepProcessLive

5、利用账号同步机制拉活

5.1 方案设计: Android系统的账号同步机制会定期同步账号,该方案的目的在于利用同步机制进行进程的拉活。

5.2 适用范围: 适用于所有的Android版本,包括forestop掉的进程也可以进行拉活。但是在最新Android版本(Android N)中系统好像对账户同步做了改动,所以该方法是否能够再用还需要进行一定的实验。

五、参考文章

【腾讯Bugly干货分享】Android进程保活招式大全