NDK20_线程轮询实现双进程守护

NDK开发汇总

一 Android应用保活的常见方式

1 提高优先级

  • 这个办法对普通应用而言,
  • 只是降低了应用被杀死的概率,但是如果真的被系统回收了,还是无法让应用自动重新启动!

2 Service重启

  • service.onStartCommand返回START_STICKY
    START_STICKY是service被kill掉后自动重启

  • 通过实验发现,如果在adb shell当中kill掉进程模拟应用被意外杀死的情况(或者用360手机卫士进行清理操作),如果服务的onStartCommand返回START_STICKY,在进程管理器中会发现过一小会后被杀死的进程的确又会出现在任务管理器中,貌似这是一个可行的办法。

  • 但是如果在系统设置的App管理中选择强行关闭应用,这时候会发现即使onStartCommand返回了START_STICKY,应用还是没能重新启动起来!

3.android:persistent=“true”

网上还提出了设置这个属性的办法,通过实验发现即使设置了这个属性,应用程序被kill之后还是不能重新启动起来的!

4.让应用成为系统应用

  • 实验发现即使成为系统应用,被杀死之后也不能自动重新启动。
  • 但是如果对一个系统应用设置了persistent=“true”,情况就不一样了
  • 实验表明对一个设置了persistent属性的系统应用,即使kill掉会立刻重启。
  • 一个设置了persistent="true"的系统应用,android中具有core service优先级,这种优先级的应用对系统的low memory killer是免疫的!

应用优先级
Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收,Android将进程分为5个等级,它们按优先级顺序由高到低依次是:

  • 空进程 Empty process
  • 可见进程 Visible process
  • 服务进程 Service process
  • 后台进程 Background process
  • 前台进程 Foreground process

5 Java实现双进程

  • 在组件中声明 android:process=":remote" 字段Android系统会为我们开辟一个进程并且把这个组件丢到该进程中,开启两个进程互相拉起

  • 如果被设置的进程名是以一个冒号开头的,则这个新的进程对于这个应用来说是私有的,当它被需要或者这个服务需要在新进程中运行的时候,这个新进程将会被创建。

  • 如果这个进程的名字是以小写字符开头的,则这个服务将运行在一个以这个名字命名的全局的进程中,
    当然前提是它有相应的权限。这将允许在不同应用中的各种组件可以共享一个进程,从而减少资源的占用。

  • 手机厂商不会允许这样的情况出现,Android系统在java层提出了双进程方案,大部分手机厂商也会针对于系统源码进行修改。导致大部分双进程不能真正开启起来

  • 但是,我们可以从linux内核下手手机厂商针对于Android系统源码容易修改,但是针对于Linux内核却无能为力,jni双进程守护,就是一个矛与盾的进化过程

二 JNI层线程轮询实现双进程守护

1 原理

  • 使用Jni,在 c端 fork进程,检测Service是否存活,若Service已被杀死,则进行重启Service.
  • 检测方式 :可以轮询获取子进程PPid,若为1, 则说明子进程被Init进程所领养,已经成为了孤儿进程.

2 jni 双进程需要面临的问题

  1. app主进程什么时候被杀死 如何监听
  2. 因为我们的进程是fork出来的,fork出来的进程父进程是app进程号当app被kill掉时,子进程被孤儿init领养 变成了空进程。怎样在监听自己变成死亡进程:
 在Linux系统下,如果使用sigaction将信号SIGCHLD的sa_flags中的SA_NOCLDSTOP选项打开,当子进程停止(STOP作业控制)时, 不产生此信号(即SIGCHLD)。不过,当子进程终止时,仍旧产生此信号(即SIGCHLD)。
  1. 如何重启服务
- am命令

wait()函数
函数功能是:父进程一旦调用了wait就立即阻塞自己,
由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

uid id 号
pid 当前的进程号
ppid 当前进程的父进程号

3 主要代码实现

#include <jni.h>
#include <string>
#include <signal.h>
#include "native-lib.h"

int user_id;
//子进程变成僵尸进程会调用这个方法
void sig_handler(int sino) {
//
    int status;
//    阻塞式函数
    LOGE("等待死亡信号");
    wait(&status);

    LOGE("创建进程");
    create_child();

}
extern "C"
JNIEXPORT void JNICALL
Java_com_dongnao_signalprocess_Wathcer_createWatcher(JNIEnv *env, jobject instance,jint userId) {
//    父进程
    user_id = userId;
//    //为了防止子进程被弄成僵尸进程   不要    1
//    struct  sigaction sa;
//    sa.sa_flags=0;
//
//    sa.sa_handler = sig_handler;
//    sigaction(SIGCHLD, &sa, NULL);
    create_child();
}

void create_child() {
    pid_t pid = fork();
//
    if (pid < 0) {

    } else if (pid > 0) {
//父进程
    } else if (pid == 0){
        LOGE("子进程开启 ");
//        开启线程轮询
        child_start_monitor();
    }

}
//相当于java  run方法
void *thread_rt(void *data){
    pid_t pid;
    while ((pid = getppid()) != 1) {
        sleep(2);
        LOGE("循环 %d ",pid);
    }
//    父进程等于1  apk被干掉了
    LOGE("重启父进程");
    execlp("am", "am", "startservice", "--user", user_id,
           "com.dongnao.signalprocess/com.dongnao.signalprocess.ProcessService", (char*)NULL);
}

void child_start_monitor() {
    pthread_t tid;
    pthread_create(&tid, NULL, thread_rt, NULL);
}

三 Demo

线程轮询实现双进程守护

发布了269 篇原创文章 · 获赞 123 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/baopengjian/article/details/105417077