如何进行进程保活,首先我们应该先分析一下进程被杀死的原因开始

Android进程被杀死的场景分析:

从 Android 的进程被杀死的场景分析,查看被杀死的原因

一.Android手机的进程回收策略

Android 的内存回收主要靠 LowMemoryKiller 完成,Low Memorry Killer的机制主要是通过进程的oom_adj和oom_score来进行内存的处理的,关于 OOM_ADJ的说明如下:

1. 每一个进程都有一个oom_adj值,取值范围[-17,15]。

2. 每一个进程都有一个oom_score值,它是根据oom_adj计算出一个值,分数越大越容易被杀死。

3. 内存紧张时,LMK基于oom_adj和oom_score值来决定是否要回收一个进程。

4. oom_adj值越小,越不容易被杀死。当 oom_adj 的值大于等于4时是比较容易被杀死的 Android进程,0-3表示不容易被杀死的Android进程,小于0的为  非Android 进程(纯 Linux 进程)尤其是-17的 native 进程不受系统管理不会被系统杀死

5. 查看oom_adj和oom_score方法:

cat proc/pid/oom_adj

cat proc/pid/oom_score

所以结合Android 的进程回收机制,若是想要不被杀死或者减少被杀死的可能性,就需要提升进程优先级,降低在内存不足被系统回收的可能性

二.killBackgroundProcesses杀死进程

ActivityManager的killBackgroundProcesses方法,可以立即杀死与指定包相关联的所有后台进程,这与内核杀死那些进程回收内存是一样的,但这些进程如果在将来某一时刻需要使用,会重新启动。该方法需要权限Android.permission.KILL_BACKGROUND_PROCESSES。源码解释如下:

三.force-stop、kill杀死进程

5.0以下源码

通过 pid 杀死进程,因此通过主进程 fork 出来的 c 进程是不会被杀死的,但是在5.0及以上源码发现,通过主进程 fork 出来的子进程也会被杀死了,话不多说直接上源码,

5.0以上源码

通过源码可以看到通过 uid 杀死进程,因此 fork 出来的子进程也同样会被杀死

总结了以上被杀死的场景分析,我们得出两种技术方案:

Ⅰ. 提升进程优先级(降低被杀死的概率)

Ⅱ.进程杀死后,拉活进程

(一)提升进程优先级的技术手段:

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

进程的重要性,划分5级:

前台进程(Foreground process)

可见进程(Visible process)

服务进程(Service process)

后台进程(Background process)

空进程(Empty process)

前台进程

用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:

托管用户正在交互的Activity(已调用Activity的onResume()方法)

托管某个Service,后者绑定到用户正在交互的 Activity

托管正在“前台”运行的Service(服务已调用startForeground())

托管正执行一个生命周期回调的Service(onCreate()、onStart()或onDestroy())

托管正执行其onReceive()方法的BroadcastReceiver

通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。 此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。

可见进程

没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:

托管不在前台、但仍对用户可见的Activity(已调用其onPause()方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。

托管绑定到可见(或前台)Activity 的Service。

可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

服务进程

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

后台进程

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

空进程

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

根据进程中当前活动组件的重要程度,Android 会将进程评定为它可能达到的最高级别。例如,如果某进程托管着服务和可见 Activity,则会将此进程评定为可见进程,而不是服务进程。

前台进程的重要性最高,依次递减,空进程的重要性最低,下面分别来阐述每种级别的进程,进程的回收策略如上,进程的介绍可详见http://www.jianshu.com/p/8a95f1f82ede?utm_source=desktop&utm_medium=timeline中介绍

目前常见的技术手段如下:

1) 1像素悬浮层

监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。

通过该方案,可以使进程的优先级在屏幕锁屏时间由4提升为最高优先级1,主要解决第三方应用及系统管理工具在检测到锁屏事件后一段时间(一般为5分钟以内)内会杀死后台进程,已达到省电的目的问题

实现方案:

首先定义 Activity,并设置 Activity 的大小为1像素:

其次,从 AndroidManifest 中通过如下属性,排除 Activity 在 RecentTask 中的显示:

最后,控制 Activity 为透明:

Activity 启动与销毁时机的控制:

2) 将Service设置为前台服务

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

从 Android2.3 开始调用 setForeground 将后台 Service 设置为前台 Service 时,必须在系统的通知栏发送一条通知,也就是前台 Service 与一条可见的通知时绑定在一起的。

对于不需要常驻通知栏的应用来说,该方案虽好,但却是用户感知的,无法直接使用。

通过实现一个内部 Service,在  KeepLiveService 和其内部 Service 中同时发送具有相同 ID 的 Notification,然后将内部 Service 结束掉。随着内部 Service 的结束,Notification 将会消失,但系统优先级依然保持为2。

具体实现方案:

(二)进程死后,拉活进程

1) 在service的onstart方法里返回 START_STICK

主要的几个返回值:

1. START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

2. START_STICKY:系统就会重新创建这个服务并且调用onStartCommand()方法,但是它不会重新传递最后的Intent对象,这适用于不执行命令的媒体播放器(或类似的服务),它只是无限期的运行着并等待工作的到来。

3. START_NOT_STICKY:直到接受到新的Intent对象,才会被重新创建。这是最安全的,用来避免在不需要的时候运行你的服务。

4. START_REDELIVER_INTENT:系统就会重新创建了这个服务,并且用最后的Intent对象调。等待中的Intent对象会依次被发送。这适用于如下载文件。

具体实现方案:

将 Service 设置为 START_STICKY,利用系统机制在 Service 挂掉后自动拉活:

如下两种情况无法拉活:

Service 第一次被异常杀死后会在5秒内重启,第二次被杀死会在10秒内重启,第三次会在20秒内重启,一旦在短时间内 Service 被杀死达到5次,则系统不再拉起。

进程被取得 Root 权限的管理工具或系统工具通过 forestop 停止掉,无法重启。

force close和一些清理软件很容易就清理掉!

2) 添加Manifest文件属性值为android:persistent=“true”

app.persistent = true不仅仅标志着此apk不能轻易的被kill掉,亦或在被kill掉后能够自动restart,并且还涉及到了进程的优先级。将被设置为CORE_SERVER_ADJ,此值为-12,而核心进程init的值为-16。当前正在前台运行的进程的值为0。如果应用能设置这个属性,那就真的可以做到保活,因为他真的可以杀不死,像系统的keyguard进程,media进程,且这些进程的adj都是负数,代表了前台activity黑屏了他们也不会死。但是这个属性需要系统shareuid,然后编译不过,因为需要系统签名!

因此,要弄这个属性需获取两个关键的信息:

(1).在apk的AndroidManifest.xml文件中设置android:persistent=true

(2).此apk需要放入到system/app目录下,成为一个systemapp

最主要的是让程序成为系统程序,这个可以做到吗?如果你技术过硬是可以尝试下的!首先弄个自动root的apk,加壳放到程序中,然后程序运行的时候,自动运行自动root的apk,获取root权限,然后在native层中,使用命令的方式把程序移到system/app目录下,成为系统程序!

3) 覆写Service的onDestroy方法

在设置里面的正在运行,注意是正在运行里面,点击关闭,会走onDestroy回调方法,你在这里可以把自己启动起来。注意是正常关闭的时候是会自己启动起来,可是使用第三方的清理软件360,root过的360,force close这些来搞,压根不会走到onDestory的方法

4) 监听一堆系统静态广播

在发生特定系统事件时,系统会发出响应的广播,通过在 AndroidManifest 中“静态”注册对应的广播监听器,即可在发生响应事件时拉活。

常用的用于拉活的广播事件包括:

但是有如下问题:

a. 广播接收器被管理软件、系统软件通过“自启管理”等功能禁用的场景无法接收到广播,从而无法自启。

b. 系统广播事件不可控,只能保证发生事件时拉活进程,但无法保证进程挂掉后立即拉活。

5) 监听第三方应用的静态广播

与接收系统广播类似,不同的是该方案为接收第三方应用广播。

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

有效程度除与系统广播一样的因素外, 第三方应用的广播属于应用私有,当前版本中有效的广播,在后续版本随时就可能被移除或被改为不外发。

6) AlarmManager唤醒

主要是实现也一个监听开机的广播,和一个周期性的闹钟,不过比较致命的是耗电量是很高的

7) 账户同步,定时唤醒

android系统里有一个账户系统,系统定期唤醒账号更新服务,同步的事件间隔是有限制的,最短1分钟,利用同步机制进行进程的拉活

难点:需要手动设置账户,你如何骗你的用户给你手动设置账户完了之后不卸载你,必须联网

添加账号和设置同步周期的代码如下:

该方案需要在 AndroidManifest 中定义账号授权与同步服务。

理论上使用与所有 Android 系统,也能解决 force-stop 的拉活,但是前提是需要联网状态下

注:最新 Android 版本(Android N)中系统好像对账户同步这里做了变动,该方法不再有效。

8) 双服务守护

这个是android里面一个特性,跨进程bind一个service之后,如果被bind的service挂掉,bind他的service会把他拉起来!可以考虑使用远程服务来实现,可是还是不能保活进程,可以被杀掉!

9) 多 APP 间互相拉起

比较常见的就是家族 app 之间互相调起,你监听到我死了,我把你拉起,之间互相拉活对方

10) Native进程拉起

原理就是通过 JNI fork出一个 c 进程,c 进程监控主进程是否存活,主要通过管道和文件监控的方式实现监控,发现主进程死后,通过调起一个 service 将主进程拉活

具体的实现方案:

此方案存在两个问题:

1.如果直接使用 JNI fork 出一个子进程,这样会存在一个内存较大问题,也就是主版占用多大内存,native 进程也占用多大内存,因此需要去写一个独立的 c 进程来降低内存大小

2.上面讲到5.0以上的系统会将根据 uid 杀进程,因此 native 进程也会被杀死,因此在5.0以上此方案失效的,

11) 双进程守护

顾名思义开启两个 native 进程进行保活,当一个 native 进程被杀死后,另一个 native 进行拉起,具体实现方案可参考:http://blog.csdn.net/marswin89/article/details/50916631

12)JobSchedule机制拉活

Android5.0 以后系统对 Native 进程等加强了管理,Native 拉活方式失效。系统在 Android5.0 以上版本提供了 JobScheduler 接口,系统会定时调用该进程以使应用进行一些逻辑操作。

具体实现方案:

通过创建一个系统任务JobInfo,添加到 JobSchedule 中,通过系统调度任务来实现拉活,force-stop 也能实现拉活

但是存在很多兼容性问题,目前在华为6.0以上不生效,以及在7.0系统上不能实现,根据官方文档说是在 Dos 安全模式下,系统调度也会被杀死

更多相关文章

  1. 浅析android通过jni控制service服务程序的简易流程
  2. 2011年Android(安卓)Camera学习笔记之一
  3. [置顶] Android(安卓)跨进程通信Aidl的使用及注意事项
  4. Android执行shell命令
  5. 开机引导程序只执行一次的方法
  6. android获取正在运行的进程
  7. Android例子—直接通过Binder的onTransact完成跨进程通信
  8. Android下读取logcat的信息
  9. Android(安卓)启动Tomcat服务报错,端口占用解决方案

随机推荐

  1. android 飞行模式分析
  2. Android(安卓)Dialog 对话框例子
  3. android 开发之电子钢琴 源码
  4. Android(安卓)通知栏系列....
  5. android 零散笔记不定期更新 v16
  6. Android(安卓)中文 API (28) —— CheckedT
  7. Android事件分发机制源码分析
  8. Gradle(三)构建任务
  9. Mono for Android(安卓)(2)-- Android应用
  10. Android(安卓)DialogFragment(1)