热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

(4.6.17.4)进程保活(三:JNI层初探):单进程单向循环式启动服务

前言我们先来看看Android4.4的源码,ActivityManagerService(源码frameworksbaseservicescoreJavacomAndro

前言

我们先来看看Android4.4的源码,ActivityManagerService(源码/frameworks/base/services/core/Java/com/Android/server/am/ActivityManagerService.java)是如何关闭在应用退出后清理内存的:

Process.killProcessQuiet(pid); 

应用退出后,ActivityManagerService就把主进程给杀死了,但是,在Android5.0中,ActivityManagerService却是这样处理的:

Process.killProcessQuiet(app.pid); 
Process.killProcessGroup(app.info.uid, app.pid);

就差了一句话,却差别很大。Android5.0在应用退出后,ActivityManagerService不仅把主进程给杀死,另外把主进程所属的进程组一并杀死,这样一来,由于子进程和主进程在同一进程组,子进程在做的事情,也就停止了…要不怎么说Android5.0在安全方面做了很多更新呢…
那么,有没有办法让子进程脱离出来,不要受到主进程的影响,当然也是可以的—— fork两次,第一个子进程独立到新的会话中,并在第二个子进程启动服务

方法一

技术关键点:开启native子进程,循环间隔的不停去启动下服务,也不判断服务是否die了。而且,如果die了,但是间隔还没到,还是不会启动服务

保活层次:基本没有实现服务和进程的相互监听,就是简单的native循环启动服务;
没有“a死,b才立即启动a;b死,a才立即启动b”的保活概念
http://download.csdn.net/detail/fei20121106/9584301

  • 结论:单杀可以杀死,force close 5.0以上无效,5.0以下部分手机无效,第三方软件下无效,且无法保证实时常驻,且费电

    • 这是要建立在保证c进程不挂的基础上,才能轮询,但是就目前来看,只有5.0以下的非国产机才会有这样的漏洞。也就是说在force close的时候,系统忽略c进程的存在,5.0以上包括5.0的哪怕源生系统也会连同c进程一起清理掉,国产机就更不用说了。就算是这样,在5.0以下的非国产机上,如果安装了获取root权限的360\cm的话,也是可以直接清理掉,也就是说会失效
    • 而且他不但不算守护,而且还是单向的,也就是说只能a保b,b保不了a;a保b也不是在b死了立刻拉起来,要等到了时间才会去拉。

配置

  • 集成library
    这里写图片描述

调用

Daemon.run(上下文, 要守护的服务, 时间间隔);

示例:

public class DaemonService extends Service {
@Override
public void onCreate() {
super.onCreate();
Daemon.run(this, DaemonService.class, Daemon.INTERVAL_ONE_MINUTE * 2);
}

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

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
/* do something here */
return super.onStartCommand(intent, flags, startId);
}
}

JNI代码分析

/*
* File : daemon.c
* Author : Vincent Cheung
* Date : Jan. 20, 2015
* Description : This is used as process daemon.
*
* Copyright (C) Vincent Chueng
*
*/


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "common.h"

#define LOG_TAG "Daemon"
#define MAXFILE 3
#define SLEEP_INTERVAL 2 * 60

volatile int sig_running = 1;

/* signal term handler */
static void sigterm_handler(int signo)
{
LOGD(LOG_TAG, "handle signal: %d ", signo);
sig_running = 0;
}

/* start daemon service */
//在新的子进程中启动服务
static void start_service(char *package_name, char *service_name)
{
/* get the sdk version */
int version = get_version();

pid_t pid;

//父进程从fork()返回时的返回值是子进程的进程号,非零正数;
//而子进程从fork()返回(严格来说子进程是从这里开始的)时的返回值是0。
//所以通过判断p的值是否为0就能知道现在运行的是父进程还是子进程。

if ((pid = fork()) <0) //第二次fork
{
exit(EXIT_SUCCESS); //fork失败
}
else if (pid == 0) //如果子进程,pid返回值是0 ,进入该逻辑[第二个子进程]
{
if (package_name == NULL || service_name == NULL)
{
LOGE(LOG_TAG, "package name or service name is null");
return;
}

/****启动android层面的service** START**/
char *p_name = str_stitching(package_name, "/");
char *s_name = str_stitching(p_name, service_name);
LOGD(LOG_TAG, "service: %s", s_name);

if (version >= 17 || version == 0)
{
int ret = execlp("am", "am", "startservice",
"--user", "0", "-n", s_name, (char *) NULL);
LOGD(LOG_TAG, "result %d", ret);
}
else
{
execlp("am", "am", "startservice", "-n", s_name, (char *) NULL);
}
/****启动android层面的service**END**/

LOGD(LOG_TAG , "exit start-service child process");
exit(EXIT_SUCCESS);
}
else//如果是父进程,返回值为pid=子进程id号 【父进程==第一个子进程】
{
//那么怎么样才能实现双向守护呢?
//首先我们想到的是fork这个函数,他会创建一个子进程,
//然后在父进程中调用waitpid()这个函数,这是一个阻塞函数,
//父进程会一直wait到子进程挂掉,才会继续向下执行,利用这个机制
//我们可以在主进程的c层fork一个子进程,然后父进程就可以监听到子进程的死亡,死亡的时候再重启子进程。

waitpid(pid, NULL, 0); //等待子进程结束,才会进入循环,也就是开始时间间隔,进入下一次循环
}
}

int main(int argc, char *argv[])
{
int i;
pid_t pid;
char *package_name = NULL;
char *service_name = NULL;
char *daemon_file_dir = NULL;
int interval = SLEEP_INTERVAL;

LOGI(LOG_TAG, "Copyright (c) 2015-2016, Vincent Cheung");

if (argc <7)
{
LOGE(LOG_TAG, "usage: %s -p package-name -s "
"daemon-service-name -t interval-time", argv[0]);
return;
}

for (i = 0; i //读命令参数
{
if (!strcmp("-p", argv[i]))
{
package_name = argv[i + 1];
LOGD(LOG_TAG, "package name: %s", package_name);
}

if (!strcmp("-s", argv[i]))
{
service_name = argv[i + 1];
LOGD(LOG_TAG, "service name: %s", service_name);
}

if (!strcmp("-t", argv[i]))
{
interval = atoi(argv[i + 1]);
LOGD(LOG_TAG, "interval: %d", interval);
}
}

/* package name and service name should not be null */
if (package_name == NULL || service_name == NULL)
{
LOGE(LOG_TAG, "package name or service name is null");
return;
}

if ((pid = fork()) <0) //第一次fork
{
exit(EXIT_SUCCESS); //fork失败
}
else if (pid == 0) //如果子进程,pid返回值是0 ,进入该逻辑【第一个子进程】
{
/* add signal */
//KILL命令的默认不带参数发送的信号就是SIGTERM.让程序有好的退出
//注册接听到SIGTERM信号时,使用sigterm_handler函数处理
signal(SIGTERM, sigterm_handler);//用于实现子进程的关闭退出

//如果parent和child运行在同一个session里,parent是会话(session)的领头进程,
//parent进程作为会话的领头进程,如果exit结束执行的话,那么子进程会成为孤儿进程,并被init收养。
//执行setsid()之后,child将重新获得一个新的会话(session)id。
//parent退出之后,将不会影响到child了。

//setsid的调用者不能是进程组组长(group leader)
//setsid()调用成功后,返回新的会话的ID
//调用setsid函数的进程成为新的会话的领头进程,并与其父进程的会话组和进程组脱离。
setsid();
/* change work directory */
//修改进程工作目录为根目录,chdir(“/”)
chdir("/");

for (i = 0; i {
close(i);
}

/* find pid by name and kill them */
int pid_list[100];
int total_num = find_pid_by_name(argv[0], pid_list);
LOGD(LOG_TAG, "total num %d", total_num);
for (i = 0; i {
int retval = 0;
int daemon_pid = pid_list[i];
if (daemon_pid > 1 && daemon_pid != getpid())
{
retval = kill(daemon_pid, SIGTERM); //【!!!】释放信号,关闭之前开启的子进程
if (!retval)
{
LOGD(LOG_TAG, "kill daemon process success: %d", daemon_pid);
}
else
{
LOGD(LOG_TAG, "kill daemon process %d fail: %s", daemon_pid, strerror(errno));
exit(EXIT_SUCCESS);
}
}
}

LOGD(LOG_TAG, "child process fork ok, daemon start: %d", getpid());

while(sig_running)
{ //过一段时间间隔去启动下服务
interval = interval select_sleep(interval, 0);

LOGD(LOG_TAG, "check the service once, interval: %d", interval);

/* start service */
//使用了waitpid(),第一个子进程会阻塞到start_service启动的第二个进程结束,才开始新循环
//避免了不断重复创建进程
start_service(package_name, service_name);
}

exit(EXIT_SUCCESS);//【!!!】循环被破坏后,自己=第一个子进程退出,保证了进程会被回收
}
else
{//如果是父进程,返回值为pid=子进程id号
/* parent process */
exit(EXIT_SUCCESS); //父进程直接退出
}
}

方法二

单进程单向循环式启动

主进程fork进程1,进程1独立出来到新的会话组,fork进程2;进程1退出,进程2开启线程,线程循环启动服务
Android 通过JNI实现守护进程,使Service服务不被杀死

JNI

#include 
#include
#include
#include
#include

#include
#include
#include
#include
#include
#include
#include

#define PROC_DIRECTORY "/proc/"
#define CASE_SENSITIVE 1
#define CASE_INSENSITIVE 0
#define EXACT_MATCH 1
#define INEXACT_MATCH 0
#define MAX_LINE_LEN 5

#include
#define TAG "daemon"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
#define LOG "Helper"


//第二个子进程的 线程函数
void thread(char* srvname) {
while(1){ //循环式的检测和启动服务
check_and_restart_service(srvname);
sleep(4);//空闲间隔
}
}

char* a;

/**
* srvname 服务名
* sd 之前创建子进程的pid写入的文件路径
*/

int start(int argc, char* srvname, char* sd) {
pthread_t id;
int ret;
struct rlimit r;

/**
* 第一次fork的作用是让shell认为本条命令已经终止,不用挂在终端输入上。
* 还有一个作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader)。
* 此时父进程是进程组组长。
*/

int pid = fork();
LOGI("fork pid: %d", pid);
if (pid <0) {
LOGI("first fork() error pid %d,so exit", pid);
exit(0);
} else if (pid != 0) {
LOGI("first fork(): I'am father pid=%d", getpid());
//exit(0);
} else { // 第一个子进程
LOGI("first fork(): I'am child pid=%d", getpid());
//int setsid = setsid();
LOGI("first fork(): setsid=%d", setsid());
umask(0); //使用umask修改文件的屏蔽字,为文件赋予跟多的权限,因为继承来的文件可能某些权限被屏蔽,从而失去某些功能,如读写

int pid = fork();
if (pid == 0) { // 第二个子进程
FILE *fp;
sprintf(sd,"%s/pid",sd);
if((fp=fopen(sd,"a"))==NULL) {//打开文件 没有就创建
LOGI("%s文件还未创建!",sd);
ftruncate(fp, 0);
lseek(fp, 0, SEEK_SET);
}
fclose(fp);
fp=fopen(sd,"rw");
if(fp>0){
char buff1[6];
int p = 0;
memset(buff1,0,sizeof(buff1));
fseek(fp,0,SEEK_SET);
fgets(buff1,6,fp); //读取一行
LOGI("读取的进程号:%s",buff1);
if(strlen(buff1)>1){ // 有值

kill(atoi(buff1), SIGTERM);
LOGI("杀死进程,pid=%d",atoi(buff1));
}
}
fclose(fp);
fp=fopen(sd,"w");
char buff[100];
int k = 3;
if(fp>0){
sprintf(buff,"%lu",getpid());
fprintf(fp,"%s\n",buff); // 把进程号写入文件
LOGI("写入。。。。。");
}
fclose(fp);
fflush(fp);

LOGI("step 2 I'am child-child pid=%d", getpid());
//step 4:修改进程工作目录为根目录,chdir(“/”).
chdir("/");
//step 5:关闭不需要的从父进程继承过来的文件描述符。
if (r.rlim_max == RLIM_INFINITY) {
r.rlim_max = 1024;
}
int i;
for (i = 0; i close(i);
}

umask(0);
//创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数
//thread:线程标识符; attr:线程属性设置; start_routine:线程函数的起始地址; arg:传递给start_routine的参数;
//返回值:成功,返回0;出错,返回-1。
ret = pthread_create(&id, NULL, (void *) thread, srvname);
if (ret != 0) {
printf("Create pthread error!\n");
exit(1);
}
int stdfd = open ("/dev/null", O_RDWR);
dup2(stdfd, STDOUT_FILENO);
dup2(stdfd, STDERR_FILENO);
} else {
exit(0);
}
}
return 0;
}

/**
* 执行命令
*/

void ExecuteCommandWithPopen(char* command, char* out_result,
int resultBufferSize) {
FILE * fp;
out_result[resultBufferSize - 1] = '\0';
fp = popen(command, "r");
if (fp) {
fgets(out_result, resultBufferSize - 1, fp);
out_result[resultBufferSize - 1] = '\0';
pclose(fp);
} else {
LOGI("popen null,so exit");
exit(0);
}
}

/**
* 检测服务,如果不存在服务则启动.
* 通过am命令启动一个laucher服务,由laucher服务负责进行主服务的检测,laucher服务在检测后自动退出
*/

void check_and_restart_service(char* service) {
LOGI("当前所在的进程pid=",getpid());
char cmdline[200];
sprintf(cmdline, "am startservice --user 0 -n %s", service);
char tmp[200];
sprintf(tmp, "cmd=%s", cmdline);
ExecuteCommandWithPopen(cmdline, tmp, 200);
LOGI( tmp, LOG);
}

jstring stoJstring(JNIEnv* env, const char* pat) {
jclass strClass = (*env)->FindClass(env, "Ljava/lang/String;");
jmethodID ctorID = (*env)->GetMethodID(env, strClass, "",
"([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*) pat);
jstring encoding = (*env)->NewStringUTF(env, "utf-8");
return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);
}


/**
* 判断是否是数字
*/

int IsNumeric(const char* ccharptr_CharacterList) {
//LOGI("IsNumeric: test.cpp/main: argc=%d",ccharptr_CharacterList);
for (; *ccharptr_CharacterList; ccharptr_CharacterList++)
if (*ccharptr_CharacterList <'0' || *ccharptr_CharacterList > '9')
return 0; // false
return 1; // true
}

//intCaseSensitive=0大小写不敏感
int strcmp_Wrapper(const char *s1, const char *s2, int intCaseSensitive) {
if (intCaseSensitive)
return !strcmp(s1, s2);
else
return !strcasecmp(s1, s2);
}

//intCaseSensitive=0大小写不敏感
int strstr_Wrapper(const char* haystack, const char* needle,
int intCaseSensitive) {
if (intCaseSensitive)
return (int) strstr(haystack, needle);
else
return (int) strcasestr(haystack, needle);
}

/**
* 通过进程名称获取pid
*/

pid_t GetPIDbyName_implements(const char* cchrptr_ProcessName,
int intCaseSensitiveness, int intExactMatch) {

char chrarry_CommandLinePath[100];
char chrarry_NameOfProcess[300];
char* chrptr_StringToCompare = NULL;
pid_t pid_ProcessIdentifier = (pid_t) - 1;
struct dirent* de_DirEntity = NULL;
DIR* dir_proc = NULL;

int (*CompareFunction)(const char*, const char*, int);

if (intExactMatch)
CompareFunction = &strcmp_Wrapper;
else
CompareFunction = &strstr_Wrapper;

dir_proc = opendir(PROC_DIRECTORY);
if (dir_proc == NULL) {
perror("Couldn't open the " PROC_DIRECTORY " directory");
return (pid_t) - 2;
}

while ((de_DirEntity = readdir(dir_proc))) {
if (de_DirEntity->d_type == DT_DIR) {

if (IsNumeric(de_DirEntity->d_name)) {
strcpy(chrarry_CommandLinePath, PROC_DIRECTORY);
strcat(chrarry_CommandLinePath, de_DirEntity->d_name);
strcat(chrarry_CommandLinePath, "/cmdline");
FILE* fd_CmdLineFile = fopen(chrarry_CommandLinePath, "rt"); //open the file for reading text
if (fd_CmdLineFile) {
LOGI("chrarry_NameOfProcess %s", chrarry_NameOfProcess);
fscanf(fd_CmdLineFile, "%s", chrarry_NameOfProcess); //read from /proc//cmdline
fclose(fd_CmdLineFile); //close the file prior to exiting the routine

chrptr_StringToCompare = chrarry_NameOfProcess;
if (CompareFunction(chrptr_StringToCompare,
cchrptr_ProcessName, intCaseSensitiveness)) {
pid_ProcessIdentifier = (pid_t) atoi(
de_DirEntity->d_name);
LOGI("processName=%d, pid=%d",cchrptr_ProcessName,pid_ProcessIdentifier);
closedir(dir_proc);
return pid_ProcessIdentifier;
}
}
}
}
}
LOGI("processName=%d, pid=%d",cchrptr_ProcessName,pid_ProcessIdentifier);
closedir(dir_proc);
return pid_ProcessIdentifier;
}

/**
* 检测服务,如果不存在服务则启动
*/

void check_and_restart_activity(char* service) {

char cmdline[200];
sprintf(cmdline, "am start -n %s", service);
char tmp[200];
sprintf(tmp, "cmd=%s", cmdline);
ExecuteCommandWithPopen(cmdline, tmp, 200);
LOGI( tmp, LOG);
}

/**
* 返回ABI给Java
*/

jstring Java_com_yyh_fork_NativeRuntime_stringFromJNI(JNIEnv* env, jobject thiz) {
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#define ABI "armeabi-v7a/NEON"
#else
#define ABI "armeabi-v7a"
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__mips__)
#define ABI "mips"
#else
#define ABI "unknown"
#endif
return (*env)->NewStringUTF(env,
"Hello from JNI ! Compiled with ABI " ABI ".");
}

/**
* jstring 转 String
*/

char* jstringTostring(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env, "java/lang/String");
jstring strencode = (*env)->NewStringUTF(env, "utf-8");
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
"(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
strencode);
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
if (alen > 0) {
rtn = (char*) malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env, barr, ba, 0);
return rtn;
}


/**
* 查找进程
*/

pid_t JNICALL Java_com_yyh_fork_NativeRuntime_findProcess(JNIEnv* env,
jobject thiz, jstring cchrptr_ProcessName) {
char * rtn = jstringTostring(env, cchrptr_ProcessName);
LOGI("Java_com_yyh_fork_NativeRuntime_findProcess run....ProcessName:%s", rtn);
//return 1;
return GetPIDbyName_implements(rtn, 0, 0); //大小写不敏感 sub串匹配
}

/**
* 启动Service
*/

void Java_com_yyh_fork_NativeRuntime_startService(JNIEnv* env, jobject thiz,
jstring cchrptr_ProcessName, jstring sdpath) {
char * rtn = jstringTostring(env, cchrptr_ProcessName); // 得到进程名称
char * sd = jstringTostring(env, sdpath);
LOGI("Java_com_yyh_fork_NativeRuntime_startService run....ProcessName:%s", rtn);
a = rtn;
start(1, rtn, sd);
}

/**
* 关闭Service
*/

void Java_com_yyh_fork_NativeRuntime_stopService() {
exit(0);
}

/**
* 启动Activity
*/

void Java_com_yyh_fork_NativeRuntime_startActivity(JNIEnv* env, jobject thiz,
jstring activityName) {
char * rtn = jstringTostring(env, activityName);
LOGI("check_and_restart_activity run....activityName:%s", rtn);
check_and_restart_activity(rtn);
}

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;

if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return result;
}
LOGI("JNI_OnLoad ......");
return JNI_VERSION_1_4;
}

使用

C/C++端关键的部分主要是以上这些,自然而然,Java端还得配合执行。
首先来看一下C/C++代码编译完的so库的加载类,以及native的调用:

package com.yyh.fork;  

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;

public class NativeRuntime {

private static NativeRuntime theInstance = null;

private NativeRuntime() {

}

public static NativeRuntime getInstance() {
if (theInstance == null)
theInstance = new NativeRuntime();
return theInstance;
}

/**
* RunExecutable 启动一个可自行的lib*.so文件
* @date 2016-1-18 下午8:22:28
* @param pacaageName
* @param filename
* @param alias 别名
* @param args 参数
* @return
*/

public String RunExecutable(String pacaageName, String filename, String alias, String args) {
String path = "/data/data/" + pacaageName;
String cmd1 = path + "/lib/" + filename;
String cmd2 = path + "/" + alias;
String cmd2_a1 = path + "/" + alias + " " + args;
String cmd3 = "chmod 777 " + cmd2;
String cmd4 = "dd if=" + cmd1 + " of=" + cmd2;
StringBuffer sb_result = new StringBuffer();

if (!new File("/data/data/" + alias).exists()) {
RunLocalUserCommand(pacaageName, cmd4, sb_result); // 拷贝lib/libtest.so到上一层目录,同时命名为test.
sb_result.append(";");
}
RunLocalUserCommand(pacaageName, cmd3, sb_result); // 改变test的属性,让其变为可执行
sb_result.append(";");
RunLocalUserCommand(pacaageName, cmd2_a1, sb_result); // 执行test程序.
sb_result.append(";");
return sb_result.toString();
}

/**
* 执行本地用户命令
* @date 2016-1-18 下午8:23:01
* @param pacaageName
* @param command
* @param sb_out_Result
* @return
*/

public boolean RunLocalUserCommand(String pacaageName, String command, StringBuffer sb_out_Result) {
Process process = null;
try {
process = Runtime.getRuntime().exec("sh"); // 获得shell进程
DataInputStream inputStream = new DataInputStream(process.getInputStream());
DataOutputStream outputStream = new DataOutputStream(process.getOutputStream());
outputStream.writeBytes("cd /data/data/" + pacaageName + "\n"); // 保证在command在自己的数据目录里执行,才有权限写文件到当前目录
outputStream.writeBytes(command + " &\n"); // 让程序在后台运行,前台马上返回
outputStream.writeBytes("exit\n");
outputStream.flush();
process.waitFor();
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
String s = new String(buffer);
if (sb_out_Result != null)
sb_out_Result.append("CMD Result:\n" + s);
} catch (Exception e) {
if (sb_out_Result != null)
sb_out_Result.append("Exception:" + e.getMessage());
return false;
}
return true;
}

public native void startActivity(String compname);

public native String stringFromJNI();

public native void startService(String srvname, String sdpath);

public native int findProcess(String packname);

public native int stopService();

static {
try {
System.loadLibrary("helper"); // 加载so库
} catch (Exception e) {
e.printStackTrace();
}
}

}

然后,我们在收到开机广播后,启动该服务。

package com.yyh.activity;  

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.yyh.fork.NativeRuntime;
import com.yyh.utils.FileUtils;
public class PhoneStatReceiver extends BroadcastReceiver {

private String TAG = "tag";

@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Log.i(TAG, "手机开机了~~");
NativeRuntime.getInstance().startService(context.getPackageName() + "/com.yyh.service.HostMonitor", FileUtils.createRootPath());
} else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
}
}


}

Service服务里面,就可以做该做的事情。

package com.yyh.service;  

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class HostMonitor extends Service {

@Override
public void onCreate() {
super.onCreate();
Log.i("daemon_java", "HostMonitor: onCreate! I can not be Killed!");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("daemon_java", "HostMonitor: onStartCommand! I can not be Killed!");
return super.onStartCommand(intent, flags, startId);
}

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

推荐阅读
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 本文介绍了一个React Native新手在尝试将数据发布到服务器时遇到的问题,以及他的React Native代码和服务器端代码。他使用fetch方法将数据发送到服务器,但无法在服务器端读取/获取发布的数据。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
author-avatar
GUOQIFENG_534
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有