Android 进程常驻(3)----native保活5.0以下方案推演过程以及代码详述

这是一个轻量级的库,配置几行代码,就可以实现在android上实现进程常驻,也就是在系统强杀下,以及360获取root权限下,clean master获取root权限下都无法杀死进程

支持系统2.3到6.0

支持大部分设备,包括三星,华为,oppo,nexus,魅族等等

可以简单对开机广播进行保护

github地址:

https://github.com/Marswin/MarsDaemon

原理分析:

Android 进程常驻(0)----MarsDaemon使用说明

Android 进程常驻(1)----开篇

Android 进程常驻(2)----细数利用android系统机制的保活手段

Android 进程常驻(3)----native保活5.0以下方案推演过程以及代码详述

Android 进程常驻(4)----native保活5.0以上方案推演过程以及代码详述

Android 进程常驻(5)----开机广播的简单守护以及总结

正文:

今天继续昨天,一鼓作气,争取这个礼拜全部写完。

上一篇文章留了一个别人的github链接,他里面的native保活实现方案也是大多数公司采用的方案。

我们先来讲一下他的方案。

他是首先开启一个c进程,将需要保活的service名字传递进去

然后定时给自己主进程发一个intent,如果主进程挂掉了,就可以顺利拉起来保证存活。

所以他只是一个没有主动权的消息轮询器,说是守护其实很勉强

而且,这是要建立在保证c进程不挂的基础上,才能轮询,但是就目前来看,只有5.0以下的非国产机才会有这样的漏洞。也就是说在force close的时候,系统忽略c进程的存在,5.0以上包括5.0的哪怕源生系统也会连同c进程一起清理掉,国产机就更不用说了。就算是这样,在5.0以下的非国产机上,如果安装了获取root权限的360\cm的话,也是可以直接清理掉,也就是说会失效。

而且他不但不算守护,而且还是单向的,也就是说只能a保b,b保不了a;a保b也不是在b死了立刻拉起来,要等到了时间才会去拉。

最后,就算把刚才说的都排除掉,在很少的一部分手机,也就是低端且没有安装安全软件的手机上,他仍然无法保证时时存活。

技术关键点:开启native子进程,定时发intent
结论:单杀可以杀死,force close 5.0以上无效,5.0以下部分手机无效,第三方软件下无效,且无法保证实时常驻

==========================分割线=========================

好了,那么怎样才是双向的守护进程呢?

第一,如果a守护b,则b挂掉的一瞬间,a就应该把b启动起来
第二,a和b应该是互相守护,无论谁挂掉,对方就把他拉起来

那么怎么样才能实现双向守护呢?首先我们想到的是fork这个函数,他会创建一个子进程,然后在父进程中调用waitpid()这个函数,这是一个阻塞函数,他会一直wait到子进程挂掉,才会继续向下执行,利用这个机制,我们可以在主进程的c层fork一个子进程,然后父进程就可以监听到子进程的死亡,死亡的时候再重启子进程。

似乎可以用这个机制改进刚刚上面分析的那个工程,因为这样的话:1,无法直接杀掉子进程。2、子进程不死,他就会按时发intent给父进程。

这样做,普通杀是没有问题。但是force close不会按照你的要求先杀孩子,等你把孩子启动起来,再杀父亲,然后坐视子进程在那不管,三方软件自不必说。那么先杀父进程的话,子进程就没办法监听到父进程的死亡吗?

有朋友要说可以利用linux的进程领养机制,如果父进程挂掉,那么子进程就会被linux的init进程领养,进程所对应的父进程id也会变成1。这的确是一个很好的标示,但是要怎样监听这个状态的变化呢?轮询获取父进程id,然后判断是否等于1?那么轮询的间隔为多少合适?1秒间隔算短算长?设为1秒的话force close掉你的时候,根本不会等到你那每秒正时正点的轮询点上,会被forceclose直接干掉,我试过更短的时间,基本要小到小于10毫秒的时间间隔,才有可能再force close的时候检测到,并成功拉起父进程。注意我的关键词,小于10毫秒!才可能!对,一秒100次的检查,才有可能,只是可能!手机待机十分钟就已经可以开始烫手,三个小时电池发出了低电量警告。代码:

(代码已经不在工程里面了,工程里的代码删除了好多,做过无数试验)

技术关键点:fork子进程 ,waitpid监听子进程 ,通过linux进程领养机制监听父进程
结论:保证单杀存活,保活效果与耗电成正比,得不偿失(5.0以上无效)

==========================分割线=========================

好,我们继续。

waitpid是阻塞函数,所以他一定是没有耗电问题的,即时性也没有问题。那么问题就集中在了子进程如何监听到父进程的死亡上面,把那个轮询替掉。

然后,我想到的是管道,linux中有多种ipc通信机制,管道是最基本的一种通信方式,且这种管道只能在父子进程间建立,于是我想是否能利用这个机制呢?在父子进程间建立管道,但是并不写入数据,只是使用阻塞方法在另一端去读取管道,这样如果对方进程挂掉,管道会被破坏,那么另一端的读取方法就会执行返回,由此确定对方挂掉然后重启对方。

代码:

这做样确实解决了耗电问题,父子进程两端的监听都是阻塞方法,耗电基本可以忽略不记,也基本实现双向守护。(以上代码不在项目工程里)

但是问题又来了

1、用ps命令发现fork出来的进程内存占用很大

2、fork出来的进程名字与父进程名字相同

原因:

1、fork函数调用的时候,会复制父进程的全部内存,因为父进程一定是我们需要保证常驻的java进程,他在初始化的时候是fork的一个zygote进程,即时在应用刚初始化的时候fork,进程里面是有一个java虚拟机的内存在里面的,最少一二十兆是有了。fork出来子进程多的内存最后都会算到我们自己应用的内存中。

2、机制如此

技术关键点:fork子进程 ,waitpid监听子进程, 管道pipe监听父进程
结论:保证双向守护,无耗电问题,fork出来的进程名字与父进程同名用户体验不好,而且有内存浪费(5.0以上无效)

==========================分割线=========================

我们继续讨论内存的问题,如何不用fork也能建立管道呢。于是我想到了运行一个二进制可执行文件,这样他是一个相对独立的进程,但是又可以建立父子进程之间的管道。将真正用来实现的子进程写到一个二进制文件中(对应文件源码/MarsDaemon/LibMarsdaemon/jni/daemon.c),这样既解决了内存问题,又可以自己给新的进程命名。

问题解决了吗?没有,直接execute一个binary文件之后

1、发现代码不再继续向下执行

2、waitpid又不能用了

原因和解决方法:

1、直接运行一个二进制文件,他会占用原进程,于是我们这里仅将fork用作一个启动binary的工具,fork终于回归到了Linus希望他作用的地方

2、父子进程间的管道是单向的,于是我们可以建两根管道。ab两个进程,建12两个管道。a进程关掉管道1的写端,堵瑟调用管道2的读取方法;b进程关掉管道2的写端,堵瑟调用管道1的读取方法。这样就可以实现双向监听。任何一方监听到对方死掉就作出相应的动作,启动对方。至此,完全摒弃开始的fork方案。

代码:

此为最终方案,代码见下

技术关键点:双管道互相监听
结论:保证双向守护,无耗电问题,无内存问题,进程名自定义(5.0以上无效)

==========================分割线=========================

好了,这就是我5.0以下的最终解决方案

下面讲一下代码

二进制文件存放在assets下面,程序第一次启动的时候会将他拷贝到手机项目/data/data/...下,然后

源码/MarsDaemon/LibMarsdaemon/src/main/java/com/marswin89/marsdaemon/strategy/DaemonStrategyUnder21.java

load对应c库,执行代码

源码/MarsDaemon/LibMarsdaemon/jni/daemon_api20.c

1、将对应的packagename,servicename以及二进制可执行文件的路径传进来

2、清理僵尸进程,就像最开始讲的,低端手机会忽略c进程,如果我们恰巧运行在低端手机上,那么c进程得不到释放会越来越多,我们称他为僵尸进程,需要清理一下

3、建立两条管道

4、执行二进制文件,将上面的参数传递进去

5、然后关掉自己管道1的写端和管道2的读端,然后阻塞读取管道1,如果读取到,则代表子进程挂掉

再来看二进制可执行文件的代码

源码/MarsDaemon/LibMarsdaemon/jni/daemon.c

二进制文件在程序启动起来的时候,将参数解析出来

关掉管道1的读端和管道2的写端,然后调用管道2的阻塞读取方法,如果执行过去,说明父进程死掉

这里用fork是为了让他的父进程id好看一些,别无他意

监听到之后的策略看下面。

==========================分割线=========================

然后说一下监听到对方进程死后的策略

你会说谁监听到对方死了,就直接拉起来就好了呀。

问题:

1、重新拉起来要重新建立双管道,子进程挂掉,父进程把他重启起来建立双管道还好说,如果父进程挂掉,子进程把父进程启动起来,他们之间就无法建立连接,而且如果中间出了差错,同步起来很费劲,于是我选择,无论谁监听到谁死了,都重启对方,然后自杀,重新初始化!

2、如果执行force close, 系统先杀父进程,子进程监听到之后拉起父进程然后自杀,但是系统杀你两个进程的间隔时间非常非常短,父进程刚起来还没来得及初始化,系统赶过来杀父进程。有的手机强杀之后很短一段时间无法拉起父进程。于是我选择用第三个进程。

第三个进程和之前的父子进程都没有任何关系,他的作用只是用做拉起常驻进程。父子进程无论谁监听到谁死,都拉起第三个进程,第三个进程负责拉起常驻进程,然后自杀。(用户实际上是看不到他的存在的,因为他可能只存活不到一秒就自杀了)

代码

/MarsDaemon/LibMarsdaemon/src/main/java/com/marswin89/marsdaemon/strategy/DaemonStrategyUnder21.java

在常驻进程初始化的时候,初始化一个alarm,保存在内存中,以便以后使用

监听到子进程死了时候使用闹钟拉起第三个进程,二进制文件监听到父进程死掉,直接用c代码发intent,见上面c代码

第三进程启动起来,就是负责把常驻进程拉起来,然后自杀掉。

==========================分割线=========================

好了,这只是5.0以下的策略,5.0以上,以及6.0都不好用

下一篇我们开始分析5.0+的策略

Android 系统(268)---native保活5.0以下方案推演过程以及代码详述相关推荐

  1. Android 系统(269)---native保活5.0以上方案推演过程以及代码详述

    Android 进程常驻(4)----native保活5.0以上方案推演过程以及代码详述 这是一个轻量级的库,配置几行代码,就可以实现在android上实现进程常驻,也就是在系统强杀下,以及360获取 ...

  2. Android 系统(266)---细数利用android系统机制的保活手段

    Android 进程常驻(2)----细数利用android系统机制的保活手段 这是一个轻量级的库,配置几行代码,就可以实现在android上实现进程常驻,也就是在系统强杀下,以及360获取root权 ...

  3. Android 进程常驻(2)----细数利用android系统机制的保活手段

    这是一个轻量级的库,配置几行代码,就可以实现在android上实现进程常驻,也就是在系统强杀下,以及360获取root权限下,clean master获取root权限下都无法杀死进程 支持系统2.3到 ...

  4. Android系统发展历程:1.0到4.0及代表机型

    今天我们来聊一聊Android系统的历史,首先我们就要先来说说Android系统这个名字的来历.Android这一次最先出现在法国作家利尔亚当在1886年发飙的科幻小说<未来夏娃>中,作者 ...

  5. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  6. Android系统的分区及img文件、移植烧写过程

    一.引言 本章介绍Android的几个分区,已经编译后生成的.img镜像文件,对于大家移植整个Android有一定的帮助.本人也首次接触Android,有不正确的地方,欢迎指正! 二.Android的 ...

  7. android耳机上报流程,Android系统中耳机按键键值上报不正确 解决过程

    [问题现象] 根据测试组同事反馈:在我们的设备上使用 JBL 品牌某款带有 3 个按键的有线耳机时,按下"音量+"键时设备会减小音量而不是增加音量,按下"音量-" ...

  8. Android 系统(274)---Anroid5.0以上进程保活方案(亲测可自行调起被杀死的app)

    Anroid5.0以上进程保活方案(亲测可自行调起被杀死的app) 实验了几种最后选择了2中组合 第一种 开启一像素activity保活,如果2个activity同时被杀死则此方案不可选. 第二种 发 ...

  9. Android系统(127)---Android6.0存储中加入总内存和系统内存项和在西语下把,换成.

    Android6.0存储中加入总内存和系统内存项 阅读数:651 平台下patches/packages/apps/Settings/里面 1.存储中加入总内存和系统内存项 在 res/values- ...

最新文章

  1. Centos-Mysql配置my.cnf内容
  2. html连接服务器文件夹,云服务器html链接到文件夹
  3. 【转】C# 中文URL编码
  4. 安装ipfs-api的命令变了,最新版的命令如下
  5. 让TortoiseGit记住帐号密码方法
  6. android平台应用技术特点,Android平台应用安全关键技术研究
  7. 数学之美:欣赏超越数e
  8. swift 对象转换_Swift类型转换–照原样,任何对象
  9. [转] 为什么用原型,为什么用闭包
  10. Android pda出入库管理,仓库PDA扫描出入库管理系统
  11. 推荐6个实用的Vue模板
  12. QC4+充电协议_坚果R1原装QC4+充电器评测:兼容USB PD3.0/PPS/QC3.0
  13. 小布老师oracle,小布老师-oracle-1
  14. vue-cli 3.x安装配置步骤详细说明文档
  15. 简单说下每一层对应的网络协议有哪些?
  16. 《学Unity的猫》——第十九集:皮皮猫上班第一天,认识游戏开发公司各个部门
  17. LLDP协议、STP协议 笔记
  18. 微信产品经理-张小龙:如何把产品做简单
  19. python to_excel 时间格式_分别用Excel和python进行日期格式转换成时间戳格式
  20. mysql批量替换字段关键词_Mysql批量替换某表里面某个字段的关键字

热门文章

  1. 【C语言】文件操作及输入输出格式(文件常用的库函数)
  2. STM32-OLED显示
  3. 单片机测量代码运行时间方法-STM32
  4. android mtk平台的fm停止搜索,【MTK平台,手机工程模式知识及方法详解】
  5. mysql中语句块当事务,MySQL事务与隔离级别
  6. c++已知2点求中垂线_电力系统负荷预测-基本方法以及分析(2)
  7. 贝叶斯网络学习Python环境搭建(pgmpy)
  8. 《深入理解分布式事务》第十章 最大努力通知型分布式事务原理
  9. NOI2016区间bzoj4653(线段树,尺取法,区间离散化)
  10. 小狼程序员:工作遐想