前言
开发一个需要常住后台的App其实是一件非常头疼的事情,不仅要应对国内各大厂商的ROM,还需要应对各类的安全管家…虽然不断的研究各式各样的方法,但是效果并不好,比如任务管理器把App干掉,服务就起不来了…
一般来说:有以下几种可行的方法来使自己的进程不被杀死
1 进程不被杀死的方法
1、提高Service的优先级:这个,也只能说在系统内存不足需要回收资源的时候,优先级较高,不容易被回收,然并卵…
2、提高Service所在进程的优先级:效果不是很明显
3、在onDestroy方法里重启service:这个倒还算挺有效的一个方法,但是,直接干掉进程的时候,onDestroy方法都进不来,更别想重启了
4、broadcast广播:和第3种一样,没进入onDestroy,就不知道什么时候发广播了,另外,在Android4.4以上,程序完全退出后,就不好接收广播了,需要在发广播的地方特定处理
5、放到System/app底下作为系统应用:这个也就是平时玩玩,没多大的实际意义。这个功能需要root,实用性不大
6、Service的onStartCommand方法,返回START_STICKY,这个也主要是针对系统资源不足而导致的服务被关闭,还是有一定的道理的。
7、开启守护进程监听,如果监听到主进程被杀死就启动主进程。但是这个在5.0以后的系统由于守护进程与主进程是同一进程组,一样的会被杀死。但是我们可以做一下修改,参考一下网上大神的代码.
思想就是:让子进程脱离出来,不要受到主进程的影响
/** * 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()); } 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) { FILE *fp; sprintf(sd,"%s/pid",sd); if((fp=fopen(sd,"a"))==NULL) { LOGI("%s文件还未创建!",sd); ftruncate(fp, 0); lseek(fp, 0, SEEK_SET); } fclose(fp); fp=fopen(sd,"rw"); if(fp>0){ char buff1[6]; int p = 0; memset(buff1,0,sizeof(buff1)); fseek(fp,0,SEEK_SET); fgets(buff1,6,fp); LOGI("读取的进程号:%s",buff1); if(strlen(buff1)>1){ kill(atoi(buff1), SIGTERM); LOGI("杀死进程,pid=%d",atoi(buff1)); } } fclose(fp); fp=fopen(sd,"w"); char buff[100]; int k = 3; if(fp>0){ sprintf(buff,"%lu",getpid()); fprintf(fp,"%s\n",buff); } fclose(fp); fflush(fp); LOGI("I'am child-child pid=%d", getpid()); chdir("/"); 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); if (ret != 0) { printf("Create pthread error!\n"); exit(1); } int stdfd = open ("/dev/null", O_RDWR); dup2(stdfd, STDOUT_FILENO); dup2(stdfd, STDERR_FILENO); } else { exit(0); } } return 0; }/** * 启动Service */ void Java_com_yyh_fork_NativeRuntime_startService(JNIEnv* env, jobject thiz, jstring cchrptr_ProcessName, jstring sdpath) { char * rtn = jstringTostring(env, cchrptr_ProcessName); char * sd = jstringTostring(env, sdpath); LOGI(“Java_com_yyh_fork_NativeRuntime_startService run….ProcessName:%s”, rtn); a = rtn; start(1, rtn, sd); }
- 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
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
这里有几个重点需要理解一下:
1、为什么要fork两次?第一次fork的作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader),而第一次调用的时候父进程是进程组组长。第二次调用后,把前面一次fork出来的子进程退出,这样第二次fork出来的子进程,就和他们脱离了关系。
2、setsid()作用是什么?setsid() 使得第二个子进程是会话组长(sid==pid),也是进程组组长(pgid == pid),并且脱离了原来控制终端。故不管控制终端怎么操作,新的进程正常情况下不会收到他发出来的这些信号。
3、umask(0)的作用:由于子进程从父进程继承下来的一些东西,可能并未把权限继承下来,所以要赋予他更高的权限,便于子进程操作。
4、chdir (“/”);作用:进程活动时,其工作目录所在的文件系统不能卸下,一般需要将工作目录改变到根目录。
5、进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。所以在最后,记得关闭掉从父进程继承过来的文件描述符。
然后,在上面的代码中开启线程后做的事,就是循环去startService(),代码如下:
void thread(char* srvname) { while(1){ check_and_restart_service(srvname); sleep(4); } }/** * 检测服务,如果不存在服务则启动. * 通过am命令启动一个laucher服务,由laucher服务负责进行主服务的检测,laucher服务在检测后自动退出 */ void check_and_restart_service(char* service) { LOGI(“当前所在的进程pid=”,getpid()); char cmdline[200]; sprintf(cmdline, “am startservice –user 0 -n %s”, service); char tmp[200]; sprintf(tmp, “cmd=%s”, cmdline); ExecuteCommandWithPopen(cmdline, tmp, 200); LOGI( tmp, LOG); }/** * 执行命令 */ void ExecuteCommandWithPopen(char* command, char* out_result, int resultBufferSize) { FILE * fp; out_result[resultBufferSize - 1] = ‘\0’; fp = popen(command, “r”); if (fp) { fgets(out_result, resultBufferSize - 1, fp); out_result[resultBufferSize - 1] = ‘\0’; pclose(fp); } else { LOGI(“popen null,so exit”); exit(0); } }
- 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
这两个启动服务的函数,里面就涉及到一些Android和Linux的命令了,这里我就不细说了。特别是am,挺强大的功能的,不仅可以开启服务,也可以开启广播等等…然后调用ndk-build命令进行编译,生成so库,ndk不会的,自行百度咯~
C/C++端关键的部分主要是以上这些,自然而然,Java端还得配合执行。
首先来看一下C/C++代码编译完的so库的加载类,以及native的调用:
package com.yyh.fork;import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File;public class NativeRuntime { private static NativeRuntime theInstance = null; private NativeRuntime() { } public static NativeRuntime getInstance() { if (theInstance == null) theInstance = new NativeRuntime(); return theInstance; } /** * RunExecutable 启动一个可自行的lib*.so文件 * @date 2016-1-18 下午8:22:28 * @param pacaageName * @param filename * @param alias 别名 * @param args 参数 * @return */ public String RunExecutable(String pacaageName, String filename, String alias, String args) { String path = "/data/data/" + pacaageName; String cmd1 = path + "/lib/" + filename; String cmd2 = path + "/" + alias; String cmd2_a1 = path + "/" + alias + " " + args; String cmd3 = "chmod 777 " + cmd2; String cmd4 = "dd if=" + cmd1 + " of=" + cmd2; StringBuffer sb_result = new StringBuffer(); if (!new File("/data/data/" + alias).exists()) { RunLocalUserCommand(pacaageName, cmd4, sb_result); sb_result.append(";"); } RunLocalUserCommand(pacaageName, cmd3, sb_result); sb_result.append(";"); RunLocalUserCommand(pacaageName, cmd2_a1, sb_result); sb_result.append(";"); return sb_result.toString(); } /** * 执行本地用户命令 * @date 2016-1-18 下午8:23:01 * @param pacaageName * @param command * @param sb_out_Result * @return */ public boolean RunLocalUserCommand(String pacaageName, String command, StringBuffer sb_out_Result) { Process process = null; try { process = Runtime.getRuntime().exec("sh"); DataInputStream inputStream = new DataInputStream(process.getInputStream()); DataOutputStream outputStream = new DataOutputStream(process.getOutputStream()); outputStream.writeBytes("cd /data/data/" + pacaageName + "\n"); outputStream.writeBytes(command + " &\n"); outputStream.writeBytes("exit\n"); outputStream.flush(); process.waitFor(); byte[] buffer = new byte[inputStream.available()]; inputStream.read(buffer); String s = new String(buffer); if (sb_out_Result != null) sb_out_Result.append("CMD Result:\n" + s); } catch (Exception e) { if (sb_out_Result != null) sb_out_Result.append("Exception:" + e.getMessage()); return false; } return true; } public native void startActivity(String compname); public native String stringFromJNI(); public native void startService(String srvname, String sdpath); public native int findProcess(String packname); public native int stopService(); static { try { System.loadLibrary("helper"); } catch (Exception e) { e.printStackTrace(); } }
- 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
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
然后,我们在收到开机广播后,启动该服务。
package com.yyh.activity;import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log;import com.yyh.fork.NativeRuntime; import com.yyh.utils.FileUtils; public class PhoneStatReceiver extends BroadcastReceiver {private String TAG = "tag"; @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { Log.i(TAG, "手机开机了~~"); NativeRuntime.getInstance().startService(context.getPackageName() + "/com.yyh.service.HostMonitor", FileUtils.createRootPath()); } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) { } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
Service服务里面,就可以做该做的事情。
package com.yyh.service;import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log;public class HostMonitor extends Service {@Override public void onCreate() { super.onCreate(); Log.i("daemon_java", "HostMonitor: onCreate! I can not be Killed!"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("daemon_java", "HostMonitor: onStartCommand! I can not be Killed!"); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent arg0) { return null; } }
- 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
当然,也不要忘记在Manifest.xml文件配置receiver和service:
2 危害
虽然我们这样做了,但是还是有许多危害的。借用知乎上的话就是:
安卓越用越卡
非常耗电
作恶全家桶
为抹黑安卓出一份力
0 0