开发一个需要常住后台的App其实是一件非常头疼的事情,不仅要应对国内各大厂商的ROM,还需要应对各类的安全管家… 虽然不断的研究各式各样的方法,但是效果并不好,比如任务管理器把App干掉,服务就起不来了…
网上搜寻一番后,主要的方法有以下几种方法,但其实也都治标不治本:
1、提高Service的优先级:这个,也只能说在系统内存不足需要回收资源的时候,优先级较高,不容易被回收,然并卵…
2、提高Service所在进程的优先级:效果不是很明显
3、在onDestroy方法里重启service:这个倒还算挺有效的一个方法,但是,直接干掉进程的时候,onDestroy()方法都进不来,更别想重启了
4、broadcast广播:和第3种一样,没进入onDestroy(),就不知道什么时候发广播了,另外,在Android4.4以上,程序完全退出后,就不好接收广播了,需要在发广播的地方特定处理
5、放到System/app底下作为系统应用:这个也就是平时玩玩,没多大的实际意义。
6、Service的onStartCommand()方法,返回START_STICKY,这个主要是针对系统资源不足而导致的服务被关闭,还是有一定的道理的。
在这里推荐一篇文章:【腾讯Bugly】Android 进程保活招式大全
应对的方法是有,实现起来都比较繁琐,而且稳定性也不好。如果你自己可以定制ROM,那就有很多种办法了,比如把你的应用加入白名单,或是多安装一个没有图标的app作为守护进程… 不过这个思想大部分程序都不适用。
那么,有没有办法在一个APP里面,开启一个子线程,在主线程被干掉了之后,子线程通过监听、轮询等方式去判断服务是否存在,不存在的话则开启服务。答案自然是肯定的,通过JNI的方式,fork()出一个子线程作为守护进程,轮询监听服务状态。守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。而守护进程的会话组和当前目录,文件描述符都是独立的。后台运行只是终端进行了一次fork,让程序在后台执行,这些都没有改变。
那么我们先来看看Android4.4的源码,ActivityManagerService(源码/frameworks/base/services/core/Java/com/Android/server/am/ActivityManagerService.java)是如何关闭在应用退出后清理内存的:
1 |
Process.killProcessQuiet(pid); |
应用退出后,ActivityManagerService就把主进程给杀死了,但是,在Android5.0中,ActivityManagerService却是这样处理的:
1 2 |
Process.killProcessQuiet(app.pid); Process.killProcessGroup(app.info.uid, app.pid); |
虽只差了一句代码,差别却很大。Android5.0在应用退出后,ActivityManagerService不仅把主进程给杀死,另外把主进程所属的进程组一并杀死,这样一来,由于子进程和主进程在同一进程组,子进程在做的事情,也就停止了…要不怎么说Android5.0在安全方面做了很多更新呢…
那么,有没有办法让子进程脱离出来,不要受到主进程的影响,当然也是可以的。那么,在C/C++层是如何实现的呢?先上关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
/** * srvname 进程名 * sd 之前创建子进程的pid写入的文件路径 */ int start(int argc, char* srvname, char* sd) { pthread_t id; int ret; struct rlimit r; int pid = fork(); LOGI("fork pid: %d", pid); if (pid < 0) { LOGI("first fork() error pid %d,so exit", pid); exit(0); } else if (pid != 0) { LOGI("first fork(): I'am father pid=%d", getpid()); //exit(0); } else { // 第一个子进程 LOGI("first fork(): I'am child pid=%d", getpid()); setsid(); LOGI("first fork(): setsid=%d", setsid()); umask(0); //为文件赋予更多的权限,因为继承来的文件可能某些权限被屏蔽 int pid = fork(); if (pid == 0) { // 第二个子进程 // 这里实际上为了防止重复开启线程,应该要有相应处理 LOGI("I'am child-child pid=%d", getpid()); chdir("/"); //<span style="font-family: Arial, Helvetica, sans-serif;">修改进程工作目录为根目录,chdir(“/”)</span> //关闭不需要的从父进程继承过来的文件描述符。 if (r.rlim_max == RLIM_INFINITY) { r.rlim_max = 1024; } int i; for (i = 0; i < r.rlim_max; i++) { close(i); } umask(0); ret = pthread_create(&id, NULL, (void *) thread, srvname); // 开启线程,轮询去监听启动服务 pan class="crayon-v">thread, srvname); // 开启线程,轮询去监听启动服务 ٟ都治标不治本:
1、提高Service的优先级:这个,也只能说在系统内存不足需要回收资源的时候,优先级较高,不容易被回收,然并卵… 2、提高Service所在进程的优先级:效果不是很明显 3、在onDestroy方法里重启service:这个倒还算挺有效的一个方法,但是,直接干掉进程的时候,onDestroy()方法都进不来,更别想重启了 4、broadcast广播:和第3种一样,没进入onDestroy(),就不知道什么时候发广播了,另外,在Android4.4以上,程序完全退出后,就不好接收广播了,需要在发广播的地方特定处理 5、放到System/app底下作为系统应用:这个也就是平时玩玩,没多大的实际意义。 6、Service的onStartCommand()方法,返回START_STICKY,这个主要是针对系统资源不足而导致的服务被关闭,还是有一定的道理的。 在这里推荐一篇文章:【腾讯Bugly】Android 进程保活招式大全 应对的方法是有,实现起来都比较繁琐,而且稳定性也不好。如果你自己可以定制ROM,那就有很多种办法了,比如把你的应用加入白名单,或是多安装一个没有图标的app作为守护进程… 不过这个思想大部分程序都不适用。 那么,有没有办法在一个APP里面,开启一个子线程,在主线程被干掉了之后,子线程通过监听、轮询等方式去判断服务是否存在,不存在的话则开启服务。答案自然是肯定的,通过JNI的方式,fork()出一个子线程作为守护进程,轮询监听服务状态。守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。而守护进程的会话组和当前目录,文件描述符都是独立的。后台运行只是终端进行了一次fork,让程序在后台执行,这些都没有改变。 那么我们先来看看Android4.4的源码,ActivityManagerService(源码/frameworks/base/services/core/Java/com/Android/server/am/ActivityManagerService.java)是如何关闭在应用退出后清理内存的:
应用退出后,ActivityManagerService就把主进程给杀死了,但是,在Android5.0中,ActivityManagerService却是这样处理的:
虽只差了一句代码,差别却很大。Android5.0在应用退出后,ActivityManagerService不仅把主进程给杀死,另外把主进程所属的进程组一并杀死,这样一来,由于子进程和主进程在同一进程组,子进程在做的事情,也就停止了…要不怎么说Android5.0在安全方面做了很多更新呢… 那么,有没有办法让子进程脱离出来,不要受到主进程的影响,当然也是可以的。那么,在C/C++层是如何实现的呢?先上关键代码:
|