Android 使用NDK编写 基于C层的守护进程

做过android开发的人应该都知道GC会在资源不够用的时候会无情的回收掉我们写的进程,但是有时候我们需要我们的进程常驻后台。这该怎么办呢?

首先说下我试过的还有网上看到过的方法吧!

1.提高优先级

 <receiver
            android:name="com.leon.test"
            android:enabled="true" >
            <intent-filter android:priority="10000" >
                <action android:name="android.intent.action.LOCALE_CHANGED" />
            </intent-filter>
 </receiver>

这个办法只是降低了应用被杀死的概率,但是如果真的被系统回收了,我们也只能对着系统呵呵哒!

扩展下,有人也写过双service守护进程,service1发现service2死了,他就复活service2。然后service2发现service1死了,他就复活service1。

这样写虽然在一部份情况下还是能坚持一会的。但是遇到一些清理软件,service1和service2都会瞬间死亡。守护功能当然也谈不上。


2.重写service.onStartCommand返回START_STICKY

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

如果在adb shell当中kill掉进程模拟应用被意外杀死的情况或者用360手机卫士进行清理操作(当然这里是没有ROOT的手机,据说360root后会在内存层面杀死程序,这个谁都挡不住的),如果服务的onStartCommand返回START_STICKY,在进程管理器中会发现被杀死的进程的确又会出现在任务管理器中。但是如果关闭进程的命令来自底层(比如系统命令adb shell am force-stop com.leon.test),这时候会发现即使onStartCommand返回了START_STICKY,应用还是没能重新启动起来!

3.让应用成为系统应用
实验发现即使成为系统应用(在烧rom的时候吧直接把APK扔到SYSTEM内),被杀死之后也不能自动重新启动。但是如果对一个系统应用设置了persistent="true",情况就不一样了。实验表明对一个设置了persistent属性的系统应用,即使kill掉会立刻重启。一个设置了persistent="true"的系统应用,在android中具有core service优先级,这种优先级的应用对系统的low memory killer是免疫的!

看了上面大家也有应该发现了一问题。只有越接近Android核心的应用才能保证在被意外杀死之后做到立刻复活。那么该怎么办呢?这里就来说一说双进程守护。网上也有人提到过双进程守护,这里给大家安利一个GitHub上面的守护进程(传送门:https://github.com/Coolerfall/AndroidAppDaemon)但是由于使用的方法并不符合我的需求,所以并没有使用。

双守护进程的原理请参考1的扩展,简而言之就是互相监视,一个死了另一个就复活他。

在写之前希望大家去了解两个命令

fork()和   execlp

OK。那开始贴代码了(注意这个是C++代码,建工程的时候请使用cpp后缀)。 

#ifndef _PROCESS_H
#define _PROCESS_H

#include <jni.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/types.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/system_properties.h>

#define LOG_TAG "Native"

#define LOGE(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)

static bool DEBUG = true;

/**
 * 功能:对父子进程的一个抽象
 * @author LeonWang
 * @date 2015-12-28
 */
class ProcessBase {
public:

	ProcessBase();

	/**
	 * 父子进程要做的工作不相同,留出一个抽象接口由父子进程
	 * 自己去实现.
	 */
	virtual void do_work() = 0;

	/**
	 * 进程可以根据需要创建子进程,如果不需要创建子进程,可以给
	 * 此接口一个空实现即可.
	 */
	virtual bool create_child() = 0;

	/**
	 * 捕捉子进程死亡的信号,如果没有子进程此方法可以给一个空实现.
	 */
	virtual void catch_child_dead_signal() = 0;

	/**
	 * 在子进程死亡之后做任意事情.
	 */
	virtual void on_child_end() = 0;

	virtual ~ProcessBase();

};

/**
 * 功能:父进程的实现
 * @author LeonWang
 * @date 2015-12-28
 */
class Parent: public ProcessBase {
public:

	Parent(JNIEnv* env, jobject jobj);

	virtual bool create_child();

	virtual void do_work();

	virtual void catch_child_dead_signal();

	virtual void on_child_end();

	virtual ~Parent();

	bool create_channel();

	/**
	 * 获取父进程的JNIEnv
	 */
	JNIEnv *get_jni_env() const;

	/**
	 * 获取Java层的对象
	 */
	jobject get_jobj() const;

};

/**
 * 子进程的实现
 * @author LeonWang
 * @date 2015-12-28
 */
class Child: public ProcessBase {
public:

	Child();

	virtual ~Child();

	virtual void do_work();

	virtual bool create_child();

	virtual void catch_child_dead_signal();

	virtual void on_child_end();

private:

	/**
	 * 处理父进程死亡事件
	 */
	void handle_parent_die();

	/**
	 * 重新启动父进程.
	 */
	void restart_parent();

	/**
	 * 线程函数,用来检测父进程是否挂掉
	 */
	void* parent_monitor();

	void start_parent_monitor();

	/**
	 * 这个联合体的作用是帮助将类的成员函数做为线程函数使用
	 */
	union {
		void* (*thread_rtn)(void*);

		void* (Child::*member_rtn)();
	} RTN_MAP;
};

extern ProcessBase *g_process;

extern const char* g_objname;

extern const char* g_type;

extern JNIEnv* g_env;

int get_version();

ProcessBase::ProcessBase() {

}

ProcessBase::~ProcessBase() {

}

Parent::Parent(JNIEnv *env, jobject jobj) {
	if (DEBUG) {
		LOGE("<<new parent instance>>");
	}
}

Parent::~Parent() {
	if (DEBUG) {
		LOGE("<<Parent::~Parent()>>");
	}

	g_process = NULL;
}

void Parent::do_work() {
}

/**
 * 子进程死亡会发出SIGCHLD信号,通过捕捉此信号父进程可以
 * 知道子进程已经死亡,此函数即为SIGCHLD信号的处理函数.
 */
static void sig_handler(int signo) {
	pid_t pid;

	int status;

//调用wait等待子进程死亡时发出的SIGCHLD
//信号以给子进程收尸,防止它变成僵尸进程
	pid = wait(&status);
	if (DEBUG) {
		LOGE("<<sig_handler>>");
	}

	if (g_process != NULL) {
		g_process->on_child_end();
	}
}

void Parent::catch_child_dead_signal() {
	if (DEBUG) {
		LOGE("<<process %d install child dead signal detector!>>", getpid());
	}

	struct sigaction sa;

	sigemptyset(&sa.sa_mask);

	sa.sa_flags = 0;

	sa.sa_handler = sig_handler;

	sigaction(SIGCHLD, &sa, NULL);
}

void Parent::on_child_end() {
	if (DEBUG) {
		LOGE("<<on_child_end:create a new child process>>");
	}

	create_child();
}

bool Parent::create_child() {
	pid_t pid;

	if ((pid = fork()) < 0) {
		return false;
	} else if (pid == 0) //子进程
			{
		if (DEBUG) {
			LOGE("<<In child process,pid=%d>>", getpid());
		}
		Child child;

		ProcessBase& ref_child = child;

		ref_child.do_work();
	} else if (pid > 0)  //父进程
			{
		if (DEBUG) {
			LOGE("<<In parent process,pid=%d>>", getpid());
		}
	}

	return true;
}

bool Child::create_child() {
//子进程不需要再去创建子进程,此函数留空
	return false;
}

Child::Child() {
	RTN_MAP.member_rtn = &Child::parent_monitor;
}

Child::~Child() {

}

void Child::catch_child_dead_signal() {
//子进程不需要捕捉SIGCHLD信号
	return;
}

void Child::on_child_end() {
//子进程不需要处理
	return;
}

void Child::handle_parent_die() {
//子进程成为了孤儿进程,等待被Init进程收养后在进行后续处理
	while (getppid() != 1) {
		usleep(500); //休眠0.5ms
	}

//重启父进程服务
	if (DEBUG) {
		LOGE("<<parent died,restart now>>");
	}

	restart_parent();
}

void Child::restart_parent() {
	if (DEBUG) {
		LOGE("<<restart_parent enter>>");
	}

	/**
	 * TODO 重启父进程,通过am启动Java空间的任一组件(service或者activity等)即可让应用重新启动
	 */
	if (strcmp(g_type, "Activity") == 0) {
			if (DEBUG) {
					LOGE("<<restart_Activity>>");
				}
			execlp("am", "am", "start","-e","daemon","triger","--user", "0", "-n", g_objname, "-a",
					"android.intent.action.VIEW", "-d", "", (char *) NULL);
		} else if (strcmp(g_type, "Service") == 0) {

//在api17之后AM命令有些不同这里需要写兼容。获取版本号的方法已经写在了下面。
<pre name="code" class="cpp">	                int g_version=get_version();

 
			if (g_version >= 17 || g_version == 0) {
				if (DEBUG) {
							LOGE("<<restart_service more than 17>>");
						}

				int ret = execlp("am", "am", "startservice","-e","daemon","triger","--user", "0", "-n",g_objname, (char *) NULL);
			} else {
				if (DEBUG) {
							LOGE("<<restart_service enter bleow 17>>");
						}

				execlp("am", "am", "startservice","-e","daemon","triger","-n", g_objname, (char *) NULL);
			}

		}

}

void* Child::parent_monitor() {
	handle_parent_die();
}

void Child::start_parent_monitor() {
	pthread_t tid;

	pthread_create(&tid, NULL, RTN_MAP.thread_rtn, this);
	pthread_join(tid, NULL);
}

void Child::do_work() {
	start_parent_monitor(); //启动监视线程
	if (DEBUG) {
		LOGE("<<start_parent_monitor>>");
	}
}
char* jstringToString(JNIEnv* env, jstring jstr) {
	char* rtn = NULL;
	jclass clsstring = env->FindClass("java/lang/String");
	jstring strencode = env->NewStringUTF("utf-8");
	jmethodID mid = env->GetMethodID(clsstring, "getBytes","(Ljava/lang/String;)[B");
	jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
	jsize array_lenth = env->GetArrayLength(barr);
	jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
	if (array_lenth > 0) {
		rtn = (char*) malloc(array_lenth + 1);
		memcpy(rtn, ba, array_lenth);
		rtn[array_lenth] = 0;
	}
	env->ReleaseByteArrayElements(barr, ba, 0);
	return rtn;
}
int get_version()
{
    char value[8] = "";
    __system_property_get("ro.build.version.sdk", value);
    return atoi(value);
}
#endif

守护进程的主程序就是这样了。

我封装好了一个可以直接使用的Jar包,有需要的可以自行下载。

地址:http://download.csdn.net/detail/kakathya/9385869

过fork分支一个子进程,子进程发现父进程死亡就重启服务或者activity。

后面有时间再写一个NDK的基础教程吧。

祝大家2016新年快乐。




  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值