NDK实现后台服务保活

现在的国产手机基本都是不能保证的 随随便便一杀就没了 复活不了;我的华为P9就不行,其他的我觉得也够呛,但是使用公司自己得MTK5.0平台, 那真是特吗和病毒没什么区别。。。

我这是在腾讯课堂里学习来得;动脑学院,挺好得一个学习android看视频得地方;

1:  java方式守护

AndroidMainfast.xml 中定义process = “:remote”字段;
手机厂商可以修改xml解析的方式,导致进程不能成功开启;普及率低

轮询;

2: ndk方式守护

  双进程保活;

宿主进程 /   守护进程
客户端    /   服务端
客户端: socket()---> connect() --->  write()  <--->  read() ----> close()
服务端: socket() --->bind() ---->listen() ---->accept()----->阻塞状态,直到客户端连接上---->  read()  <---->  write()  ----> read()这里会收到客户端close()时的消息------>close();

linux  socket

等待连接;与java不同这里是分开的;
读取消息;分开的;

socket跨进程   基于文件 读写;

额外一个比较有趣的打电话:
am start -a android .intent.action.CALL -d tel:10086

首先先来个service,记得注册;

/**
 * 所守护的service
 */
public class ProcessService extends Service {

    private static final String TAG = ProcessService.class.getSimpleName();
    private int i;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
//        return super.onStartCommand(intent, flags, startId);

        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    // 这里是JNI;
        Watcher watcher = new Watcher();
        watcher.createWatcher(String.valueOf(Process.myUid()));
        watcher.connectMonitor();

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                Log.e(TAG, "service is opening ... " + i);
                i++;
        // 这里只是在手机的一个目录下一直写东西;;;就不贴了;
                FileUtils.JsonWrite(Environment.getExternalStorageDirectory() + "//textDoubleProtect.txt", i + "\n");
            }
        }, 0, 1000 * 3);
    }

    @Override
    public void onDestroy() {
        // 这里直接杀掉是不走这里的;
        Log.e(TAG, "service   onDestroy");
        super.onDestroy();
    }
}

watcher类;

/**
 * Created by bysd-2 on 2018/6/13.
 */
public class Watcher {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
    public native void createWatcher(String userId); // jni

    public native void connectMonitor();

}

jni的代码;
这里是头文件;

//
// Created by bysd-2 on 2018/6/13.
//

#ifndef DOUBLEPROCESSPROTECT_NATIVE_LIB_H
#define DOUBLEPROCESSPROTECT_NATIVE_LIB_H

#endif //DOUBLEPROCESSPROTECT_NATIVE_LIB_H

// 所需头文件
#include <sys/select.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
#include <android/log.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <linux/signal.h>
#include <android/log.h>
#define  LOG_TAG "tuch"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

// 方法在c里面;
void child_do_work();
int child_create_channel();
void child_listen_msg();

//

#include <jni.h>
#include <string>

#include <unistd.h>

// child_create_channel()
#include "native-lib.h"

const char *PATH = "****************************这里自己定义路径  如  /data/data/com.example.demo/...";
int m_child;
const char *userId;

/**
 * 创建socket;
 */
void child_do_work() {
    // 守护进程为服务端;
    // open socket
    if (child_create_channel()) {
        child_listen_msg();
    }
}

/**
 * 创建服务端socket
 */
void child_listen_msg() {
    fd_set rfds;
    struct timeval timeout = {3, 0};

    while (1) {

        // 清空内容,缓存,端口号等等;
        FD_ZERO(&rfds);
        // 赋值
        FD_SET(m_child, &rfds);
        // 设置客户端最大数目+1;
        int r = select(m_child + 1, &rfds, NULL, NULL, &timeout);
//        LOGE("读取消息前  %d  ", r);

        if (r > 0) { // 选择了客户端
            // 缓冲区
            char pkg[256] = {0};

            // 保证所读到的信息是指定apk的客户端;
            if (FD_ISSET(m_child, &rfds)) {
                int result = read(m_child, pkg, sizeof(pkg));
                LOGE("userId  %s  ",userId);
                // 开启服务;
                execlp("am", "am", "startservice", "--user", userId,
                       "包名/service的全类名",
                       (char *) NULL);

                break;
            }
        }
    }
}

/**
 * 服务端读取信息;
 * 客户端;
 */
int child_create_channel() {
    // socket 第一步;
    int listenfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    //
    unlink(PATH);

    // bind
    struct sockaddr_un addr;
    // 清空内存,全部置0;
    memset(&addr, 0, sizeof(sockaddr_un));
    addr.sun_family = AF_LOCAL;
    //
    int connfd = 0;

    // 复制赋值;
    strcpy(addr.sun_path, PATH);
    //
    int a = bind(listenfd, (const sockaddr *) &addr, sizeof(sockaddr_un));
    if (a < 0) {
        // 绑定错误;
        LOGE("绑定错误!!!");
        return 0;
    }
    listen(listenfd, 5);//  监听最大客服端数目: 5

    // 保证 宿主进程连接成功;
    while (1) {
        // 返回值为客户端的地址;  阻塞式函数;
        connfd = accept(listenfd, NULL, NULL); // 执行完 errno会有值;
        if (connfd < 0) { // 连接失败;
            if (errno == EINTR) {  // errno为linux内置成员变量;accept执行完会赋值;
                continue;
            } else {
                LOGE("读取错误!!!");
                return 0;
            }
        }
        m_child = connfd;
        break;
    }
    return 1;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_htnova_doubleprocessprotect_Watcher_connectMonitor(JNIEnv *env, jobject instance) {

    int socked;
    // 内存区域
    struct sockaddr_un addr;

    while (1) {
        LOGE("客户端  父进程开始连接");
        socked = socket(AF_LOCAL, SOCK_STREAM, 0); // 要与服务端的配置一样;
        if (socked < 0) {
            LOGE("客户端连接失败");
            return;
        }
        // 清空内存,全部置0;
        memset(&addr, 0, sizeof(sockaddr));
        addr.sun_family = AF_LOCAL;

        strcpy(addr.sun_path, PATH);

        if (connect(socked, (const sockaddr *) &addr, sizeof(sockaddr_un)) < 0) {
            LOGE("客户端连接失败");
            close(socked);
            sleep(1);
            // 再来下一次;
            continue;
        }
        LOGE("客户端连接成功");
        break;
    }
}

extern "C"
JNIEXPORT
void JNICALL
Java_com_htnova_doubleprocessprotect_Watcher_createWatcher(JNIEnv *env, jobject instance,
                                                           jstring userId_) {
    userId = env->GetStringUTFChars(userId_, 0);

    // TODO  创建socket;
    // linux 开启双进程
    pid_t pid = fork();
    if (pid < 0) {
        // 失败;
    }
    else if (pid == 0) {
        // 子进程;  守护进程
        child_do_work();
    } else if (pid > 0) {
        // 父进程;
    }
    env->ReleaseStringUTFChars(userId_, userId);
}

到这里就可以了,c里面的东西可能比较难懂,但主要看service里面的东西 基本能知道大概;不懂得话copy一下 跑起来就知道怎么回事了;记录一下 虽然现在限制的很多 基本白费劲 但是也学习了一种方式; 好了 下班!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值