守护进程拉起App进程的日志现象
ActivityManager pid-1626 I startProcessLocked com.ayst.helloapptype
java.lang.Throwable
at com.android.server.am.ProcessList.startProcessLocked(ProcessList.java:1860)
at com.android.server.am.ProcessList.startProcessLocked(ProcessList.java:2655)
at com.android.server.am.ProcessList.startProcessLocked(ProcessList.java:2801)
at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:3076)
at com.android.server.am.ActivityManagerService$LocalService.startProcess(ActivityManagerService.java:19655)
at com.android.server.wm.ActivityTaskManagerService$$ExternalSyntheticLambda20.accept(R8$$SyntheticClass:0)
at com.android.internal.util.function.pooled.PooledLambdaImpl.doInvoke(PooledLambdaImpl.java:363)
at com.android.internal.util.function.pooled.PooledLambdaImpl.invoke(PooledLambdaImpl.java:204)
at com.android.internal.util.function.pooled.OmniFunction.run(OmniFunction.java:87)
at android.os.Handler.handleCallback(Handler.java:959)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.os.HandlerThread.run(HandlerThread.java:85)
at com.android.server.ServiceThread.run(ServiceThread.java:46)
为啥我想玩这个保活的技术呢?因为我最近有时间研究反保活,反正随便玩一玩哈。
引言
在Android开发中,后台服务保活一直是难题。随着系统版本升级,单纯依靠Java层的Service越来越容易被系统回收。本文将介绍一种Native层+Service双进程守护的方案,通过JNI fork子进程监控主进程状态,实现高存活率的后台任务管理
一、方案核心原理
1.双进程守护机制
- Native层:通过JNI调用Linux的fork()创建子进程,定时检查主进程存活状态
- Java层:START_STICKYService提供基础保活能力,与Native进程形成双向守护
2.技术亮点
- 跨语言协作:Kotlin调用Native代码,实现Linux级进程管理
- 低功耗轮询:Native层使用sleep(interval)控制CPU占用
- 快速恢复:进程异常退出时,通过am startservice命令秒级重启
二、完整实现步骤
1. 配置Native层(C代码)
#include <jni.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <android/log.h>
#define LOG_TAG "Daemon"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
// 检查进程是否存活
int is_process_alive(const char *package_name) {
char cmd[128];
sprintf(cmd, "ps | grep %s", package_name);
FILE *fp = popen(cmd, "r");
if (fp) {
char buf[256];
while (fgets(buf, sizeof(buf), fp)) {
if (strstr(buf, package_name)) {
pclose(fp);
return 1;
}
}
pclose(fp);
}
return 0;
}
// 拉起Java进程
void launch_app(const char *package_name, const char *service_name) {
char cmd[256];
sprintf(cmd, "am start -n %s/%s", package_name, service_name);
system(cmd);
LOGD("Launching app: %s", cmd);
}
// 启动Service替代Activity
void launch_service(const char *package_name, const char *service_name) {
char cmd[256];
sprintf(cmd, "am startservice -n %s/%s", package_name, service_name);
system(cmd);
LOGD("Launching service: %s", cmd);
}
// 守护进程主逻辑
void run_daemon(const char *package_name, const char *service_name, int interval) {
while (1) {
if (!is_process_alive(package_name)) {
LOGD("Process dead, relaunching service...");
launch_service(package_name, service_name);
}
sleep(interval);
}
}
// JNI接口
JNIEXPORT void JNICALL
Java_com_ayst_helloapptype_NativeDaemon_startDaemon( // 修正包路径
JNIEnv *env,
jobject thiz,
jstring java_package_name,
jstring java_service_name,
jint interval
) {
const char *package_name = (*env)->GetStringUTFChars(env, java_package_name, 0);
const char *service_name = (*env)->GetStringUTFChars(env, java_service_name, 0);
pid_t pid = fork();
if (pid < 0) {
LOGD("Fork failed!");
} else if (pid == 0) {
// 子进程成为守护进程
setsid(); // 创建新会话
run_daemon(package_name, service_name, (int)interval);
_exit(0);
}
// 父进程继续执行Java逻辑
(*env)->ReleaseStringUTFChars(env, java_package_name, package_name);
(*env)->ReleaseStringUTFChars(env, java_service_name, service_name);
}
2. 声明JNI接口(Kotlin)
package com.ayst.helloapptype
import android.util.Log
object NativeDaemon {
init {
System.loadLibrary("daemon-lib")
}
// Native 声明
external fun startDaemon(packageName: String?, serviceName: String?, interval: Int)
fun start(packageName: String?, serviceName: String?) {
// 启动守护进程,每30秒检查一次
startDaemon(packageName, serviceName, 30)
Log.d("sufadi", "NativeDaemon start:" + serviceName)
}
}
注意:需在AndroidManifest.xml声明服务:
<service
android:name=".FadiDaemonService"
android:exported="true" /> <!-- 允许跨进程启动 -->
4. CMake配置
cmake_minimum_required(VERSION 3.4.1)
add_library(
daemon-lib # 库名
SHARED # 动态库
daemon.c # 源文件
)
target_link_libraries(daemon-lib log) # 链接Android日志库
5. 调用方式NativeDaemon.start(getPackageName(), FadiDaemonService::class.java.name)
package com.ayst.helloapptype
import android.app.Activity
import android.os.Bundle
import android.util.Log
class MainActivity : Activity() {
private val TAG = "sufadi"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "Daemon start")
NativeDaemon.start(getPackageName(), FadiDaemonService::class.java.name)
}
override fun onDestroy() {
super.onDestroy()
}
}
应用宝也是这样干的哈
理论 Android Daemon 在处理后台任务、长期运行的服务时非常有用。但是我使用是系统开发角度,我的工作是反保活哈。所以对我来说拦截自启动的技术也是一样的,本质就是一个return的事情解除保活状态。