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

来源:互联网 发布:RBF神经网络算法 编辑:程序博客网 时间:2024/05/21 22:47

做过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 0