进程保活方案学习

进程保活方案

进程保活主要有两个方案

  • 提高进程优先级,降低死亡几率
  • 在进程被杀死后进行拉活

进程为什么会死亡

从Linux kernel 2.6.11开始,内核提供了进程的OOM控制机制。当系统出现内存不足的情况时,内核可以根据进程的oom_adj值,
来选择杀死一些进程,以回收内存。Android系统正式基于这一原理进行进程管理。

这里的OOM不是我们应用程序内的oom异常,而是整个系统内存不足

内存管理乃至进程管理都是kernel内核层直接管理地,但是oom_adj的值确是由Framework层中的AMS来管理更新的。我们知道Android的四大组件都是由AMS直接管理的,所以AMS能够悉知四大组件的生命周期,当发生可能会影响进程优先级的事件时,AMS计算出相应改变后的进程oom_adj值,同时修该oom_adj的配置参数,当内存紧张kernel进行内容回收时,则会根据对应的oom_adj优先回收oom_adj值高的进程。这个过程可由下图表示

当然oom_adj只是影响进程被回收的一个因素,除此之外进程占内存和进程存活时间也是比较重要的因素,这里就不一一赘述。

提高进程优先级

Android中各个adj对应的进程状态如下图所示

image

正常而言我们的后台程序是在9-15之间出于一个比较危险的级别,减少进程被杀死概率一是想办法提高进程优先级,减少进程在内存不足等情况下被杀死的概率,同时减小内存的占用也会在oom_adj相同的情况下占据优势,不会优先被回收。提高进程优先级的偏方主要如下

1像素Activity

监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。

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

具体实现如下

//OnePixelActivity
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    window.setGravity(Gravity.LEFT or Gravity.TOP)
    window.attributes.x = 0
    window.attributes.y = 0
    window.attributes.height = 1 //设置1像素点
    window.attributes.width = 1 //设置1像素点
    finishReceiver = FinishReceiver() //设置结束广播
    val filter = IntentFilter()
    filter.addAction(ACTION_FINISH)
    LocalBroadcastManager.getInstance(this).registerReceiver(finishReceiver, filter)
}

//接受锁屏广播
fun register(context : Context) {
    screenReceiver = ScreenReceiver()
    var filter = IntentFilter()
    filter.addAction(Intent.ACTION_SCREEN_OFF)
    filter.addAction(Intent.ACTION_SCREEN_ON)
    context.registerReceiver(screenReceiver,filter)
    mContext = WeakReference(context)
}

//根据广播开启和结束OnePixelActivity
override fun onReceive(context: Context?, intent: Intent?) {
    when(intent?.action){
        Intent.ACTION_SCREEN_OFF->{
            var onePixelIntent = Intent(context,OnePixelActivity::class.java)
            onePixelIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            context?.startActivity(onePixelIntent)
        }
        Intent.ACTION_SCREEN_ON->{
            mContext.get()?.sendBroadcast(Intent(OnePixelActivity.ACTION_FINISH))
        }
        else->{}
    }
}

前台服务

服务进程优先级虽然高于后台进程,但是还是处于比较危险的状态,绑定前台notification之后,可以把优先级提高到可见进程,极大减小进程被杀死的可能性,但是这样用户可以在通知栏看到我们的notification,那有没有两全齐美的方法呢?当然有!

API18以前直接传入一个空的Notifacation即可

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    startForeground(NOTIFICATION_ID, Notifacation())
    return super.onStartCommand(intent, flags, startId)
}

API18之后系统修复这个漏洞,但是道高一次魔高一丈,在我们的service绑定Notifacation之后,实现一个内部 Service,其内部 Service 中同时发送具有相同 ID 的 Notification,然后将内部 Service 结束掉。随着内部 Service的结束,Notification 将会消失。进程优先级不回变。

 class InnerService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startForeground(111, generateNotification(this))
        stopForeground(true)
        stopSelf()
        return super.onStartCommand(intent, flags, startId)
    }
}

进程拉活

广播拉活

接受各种系统广播和第三方广播,原理很简单,但是拉活时间不确定,如何获取第三方广播也是一个问题,应该算是一种不是很稳定的手段。

native进程拉活

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

但是由于5.0以后ActivityManagerService会这样处理的:

Process.killProcessQuiet(app.pid);  
Process.killProcessGroup(app.info.uid, app.pid);

同时杀死进程和进程fork的子进程,所以方法已经无效了。加上本人jni辣鸡,这里就不展示了。

JobSheduler拉活

JobSheduler是Android5.0之后提供的API,JobScheduler API允许开发者在符合某些条件时创建执行在后台的任务。基本上就是我们可以把耗时任务扔给JobSheduler,当条件满足,我们的任务就会执行,本意是好的,但是却被用来干坏事

//使用SystemService获取JobScheduler对象
var jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
var jobId = 111 //唯一jobId
//制定
var name = ComponentName(context.applicationContext, KeepAliveJobService::class.java)
jobScheduler.schedule(JobInfo.Builder(jobId, name)
        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)//任务网络要求
        .setPeriodic(30000)//周期性执行,不能与下面俩个同时使用
//      .setMinimumLatency(3000)//任务最少开始的时延
//      .setOverrideDeadline(50000)//到达deadline即使条件不满足也会执行
        .setRequiresCharging(false)//是否需要正在充电
        .setRequiresDeviceIdle(false)//是否需要手机闲置
        .setRequiresBatteryNotLow(true)//是否手机电量高
        .setRequiresStorageNotLow(false)//是否需要手机存储不低
        .setPersisted(true)//任务是否需要被持久化(重启后继续)
        .build())

我们的JobService

class KeepAliveJobService: JobService() {
    override fun onStopJob(params: JobParameters?): Boolean {

       return false
    }

    override fun onStartJob(params: JobParameters?): Boolean {
        startService()//启动我们的服务
        return true
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return START_STICKY
    }

}

后记

保活的手段比较多,但是需要大家合理使用,共同维护一个Android生态,同时学习进程保活也算是从另一角度去看Android的进程管理。

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容