Android 守護進程 (NDK Service 單工機制)

個人筆記,學習於騰訊課堂

(1.模擬器7.0系統成功保活  2.真機小米8 8.0android系統 無效)

0.項目結構

1.在項目中配置ndk(略)

CMakeList.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             native-lib.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

2.jni調用類 Watcher.kt

class Watcher {

    init {
        System.loadLibrary("native-lib")
    }

    public external fun createWatcher(userId: String)

    public external fun  connectMonitor()

}

3.後臺服務 ProcessService.kt

package com.example.socketprocess

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.Process
import android.util.Log
import java.util.*

/**
 * Just :
 * @author by Zian
 * @date on 2019/07/02 10
 */
class ProcessService : Service() {

    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        val watcher = Watcher()
        watcher.createWatcher(Process.myUid().toString())
        watcher.connectMonitor()
        var i = 0
        val timer = Timer()
        timer.scheduleAtFixedRate(object : TimerTask() {
            override fun run() {
                i++
                Log.w("ProcessService:", "服務開啓中 $i")
            }
        }, 0, 1000 * 3)
    }
}

3.1頭文件配置 native-lib.h

//
// Created by Zian on 2019/7/2.
//

#ifndef SOCKETPROCESS_NATIVE_LIB_H
#define SOCKETPROCESS_NATIVE_LIB_H

#endif //

#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/types.h>
#include <errno.h>
#include <stdlib.h>
#include <linux/signal.h>
#include <android/log.h>
#include <unistd.h>
#include <sys/un.h>
#define LOG_TAG "tuch"
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)


void child_do_work();
int child_create_channel();
void child_listen_msg();

3.2 ndk代碼 native-lib.cpp

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

const char *userId;
int m_child;
const char *PATH = "data/data/com.example.socketprocess/my.sock";

void child_do_work() {
    //開啓socket 服務端
    if (child_create_channel()) {
        child_listen_msg();
    }
    //等待連接
    //讀取消息

}

/**
 * 服務端讀取信息
 */
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);

        if (r > 0) {
            //緩衝區
            char pkg[256] = {0};
            //保證所讀apk 是指定apk
            if (FD_ISSET(m_child, &rfds)) {
                //阻塞式函數
                int result = read(m_child, pkg, sizeof(pkg));
                LOGE("userId %s", userId);
                //如果socket 斷開 便不再阻塞
                //開啓服務 (com.example.socketproces爲應用包名)
                execlp("am", "am", "startservice", "--user", userId,
                       "com.example.socketprocess/com.example.socketprocess.ProcessService",
                       (char *) NULL);
                break;
            }
        }
    }
}


/**
 * 創建服務店Socker
 * 客戶端 apk進程
 * @return
 */
int child_create_channel() {
    //創建一個socker 協議 類型
    //socket 通過文件端口讀寫 可以跨進程
    int listenfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    unlink(PATH);
    //綁定 socket標識 流體
    //addr ---< 內存區域
    struct sockaddr_un addr;

    //清空內存 =
    memset(&addr, 0, sizeof(sockaddr_un));
    //指定協議
    addr.sun_family = AF_LOCAL;

    int connfd = 0;
    strcpy(addr.sun_path, PATH);

    if (bind(listenfd, reinterpret_cast<const sockaddr *>(&addr), sizeof(sockaddr_un)) < 0) {
        LOGE("綁定錯誤");
        return 0;
    }

    listen(listenfd, 5);
    while (1) {
        //返回值 客戶端的地址 阻塞式函數
        if (connfd = accept(listenfd, NULL, NULL) < 0) {
            if (errno == EINTR) {
                continue;
            } else {
                LOGE("讀取錯誤");
                return 0;
            }
        }
        m_child = connfd;
        LOGE("APK 父進程連接上了 %d ", m_child);
        break;
    }

    return 1;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_socketprocess_Watcher_createWatcher(
        JNIEnv *env,
        jobject instance,
        jstring userId_) {
    userId = env->GetStringUTFChars(userId_, 0);
    //開雙進程
    pid_t pid = fork();
    if (pid < 0) {
    } else if (pid == 0) {
        //子進程 : 當前進程的守護進程
        child_do_work();
    } else {
        //父進程
    }

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_socketprocess_Watcher_connectMonitor(
        JNIEnv *env,
        jobject instance) {
    LOGE("開始連接客戶端...");
    //apk進程 客戶端sock
    int socked;
    struct sockaddr_un addr;
    while (1) {
        socked = socket(AF_LOCAL, SOCK_STREAM, 0);
        if (socked < 0) {
            return;
        }
        //清空內存 =
        memset(&addr, 0, sizeof(sockaddr));
        //指定協議
        addr.sun_family = AF_LOCAL;
        strcpy(addr.sun_path, PATH);

        if (connect(socked, reinterpret_cast<const sockaddr *>(&addr), sizeof(sockaddr_un)) < 0) {
            LOGE("連接失敗");
            close(socked);
            sleep(1);
            //再來下一次連接
            continue;
        }
        LOGE("連接成功");
        break;
    }

}

4.配置及開啓服務 AndroidManifest.xml 及 MainActivity.kt

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.socketprocess">

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service android:name=".ProcessService"/>
    </application>

</manifest>
package com.example.socketprocess

import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val intentService = Intent(this, ProcessService::class.java)
        startService(intentService)
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章