個人筆記,學習於騰訊課堂
(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)
}
}