不被杀掉的service如果不root的话,我觉得是不现实的。因为各个系统之间的差异,还有各种安全软件的存在。这里说一下目前为止,可以想到的方法。
1、尽量提高优先级
android:priority = "1000"
android:persistent="true"
设置成前台服务
返回START_STICKY
销毁时自启
监听开机,锁屏,电量改变广播
2、自身使用aidl进行双服务守护
这个时候在正在运行设置中强行结束一个,另一个还可以起来。但是如果从任务管理强行停止就没办法
3、无launch图标的app,运行守护服务
这样可以保证从任务管理强行停止中恢复
4、在守护应用的服务中,自身通过jni层面双线程进行守护
其实做到这一步,问题就出在jni守护的服务还是会被系统自己回收掉,貌似是因为现在的android系统是杀线程组来实现的,而不是以前杀单一线程
关于ndk的双线程守护,网上也有不少,但是缺少必要的头文件清单,可能有人对C++不熟,这里记录一个从网上寻找,自己调试成功的代码
#include <string.h>
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "keymatch", __VA_ARGS__)
extern "C"{
//jstring转string
char* jstringTostring(JNIEnv* env, jstring jstr)
{
LOGD("字符串转换");
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;
}
//执行linux命令行
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 {
LOGD("popen null,so exit");
exit(0);
}
}
//重启服务
void check_and_restart_service(char* service) {
LOGD("当前所在的进程pid=%d,%s",getpid(),service);
char cmdline[200];
sprintf(cmdline, "am startservice --user 0 -n %s", service);
char tmp[200];
sprintf(tmp, "cmd=%s", cmdline);
ExecuteCommandWithPopen(cmdline, tmp, 200);
}
//该线程用来轮询检查服务状态
void* thread(void* srvname) {
LOGD("loop thread start!\n");
while(1){
check_and_restart_service((char*) srvname); // 应该要去判断service状态,这里一直restart 是不足之处
sleep(10000);
}
return NULL;
}
int start(int argc, char* srvname, char* sd) {
LOGD("服务名%s",srvname);
//linux的线程,在pthread.h中声明,实际是一个long变量
pthread_t id;
int ret;
struct rlimit r;
//linux中通过fork创建子线程,需要头文件为sys/types.h,unistd.h
int pid = fork();
LOGD("fork pid: %d", pid);
if (pid < 0) {
LOGD("first fork() error pid %d,so exit", pid);
//结束进程,包含在stdlib.h头文件中
exit(0);
} else if (pid != 0) {
LOGD("first fork(): I'am father pid=%d", getpid());
//exit(0);
} else { // 第一个子进程
LOGD("first fork(): I'am child pid=%d", getpid());
setsid();
LOGD("first fork(): setsid=%d", setsid());
umask(0); //为文件赋予更多的权限,因为继承来的文件可能某些权限被屏蔽,包含在sys/types.h和sys/stat.h头文件中
int pid = fork();
if (pid == 0) { // 第二个子进程
// 这里实际上为了防止重复开启线程,应该要有相应处理
LOGD("I'am child-child pid=%d", getpid());
chdir("/"); //<span style="font-family: Arial, Helvetica, sans-serif;">修改进程工作目录为根目录,chdir(“/”)</span>
//关闭不需要的从父进程继承过来的文件描述符。
//RLIM_INFINITY表示无穷大,包含在sys/resource.h头文件中,实质是无符号长整型最大位数个1,0xffffff... ~0UL
if (r.rlim_max == RLIM_INFINITY) {
r.rlim_max = 1024;
}
int i;
for (i = 0; i < r.rlim_max; i++) {
close(i);
}
umask(0);
LOGD("创建守护线程");
ret = pthread_create(&id, NULL,thread, srvname); // 开启线程,轮询去监听启动服务
if (ret != 0) {
printf("Create pthread error!\n");
exit(1);
}
//O_RDWR包含在fcntl.h头文件中
int stdfd = open ("/dev/null", O_RDWR);
dup2(stdfd, STDOUT_FILENO);
dup2(stdfd, STDERR_FILENO);
} else {
exit(0);
}
}
return 0;
}
void Java_com_xf_ztimeGuid_receiver_Receiver_startServer(JNIEnv* env,jobject thiz,
jstring cchrptr_ProcessName, jstring sdpath){
LOGD("jni start!!!!!!\n");
//得到进程名称
char * rtn = jstringTostring(env, cchrptr_ProcessName);
LOGD("complete convert jstring %s!\n",rtn);
//得到文件路径
char * sd = jstringTostring(env, sdpath);
LOGD("Java_com_xf_ztime_base_activity_AlarmActivity_startServer run....ProcessName:%s", rtn);
start(1, rtn, sd);
}
}