做过android开发的人应该都知道GC会在资源不够用的时候会无情的回收掉我们写的进程,但是有时候我们需要我们的进程常驻后台。这该怎么办呢?
首先说下我试过的还有网上看到过的方法吧!
1.提高优先级
这个办法只是降低了应用被杀死的概率,但是如果真的被系统回收了,我们也只能对着系统呵呵哒!
扩展下,有人也写过双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 #include #include #include #include #include #include #include #include #include #include #include #include #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("<>");
}
}
Parent::~Parent() {
if (DEBUG) {
LOGE("<>");
}
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("<>");
}
if (g_process != NULL) {
g_process->on_child_end();
}
}
void Parent::catch_child_dead_signal() {
if (DEBUG) {
LOGE("<>", 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("<>");
}
create_child();
}
bool Parent::create_child() {
pid_t pid;
if ((pid = fork()) < 0) {
return false;
} else if (pid == 0) //子进程
{
if (DEBUG) {
LOGE("<>", getpid());
}
Child child;
ProcessBase& ref_child = child;
ref_child.do_work();
} else if (pid > 0) //父进程
{
if (DEBUG) {
LOGE("<>", 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("<>");
}
restart_parent();
}
void Child::restart_parent() {
if (DEBUG) {
LOGE("<>");
}
/**
* TODO 重启父进程,通过am启动Java空间的任一组件(service或者activity等)即可让应用重新启动
*/
if (strcmp(g_type, "Activity") == 0) {
if (DEBUG) {
LOGE("<>");
}
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命令有些不同这里需要写兼容。获取版本号的方法已经写在了下面。
int g_version=get_version();
if (g_version >= 17 || g_version == 0) {
if (DEBUG) {
LOGE("<>");
}
int ret = execlp("am", "am", "startservice","-e","daemon","triger","--user", "0", "-n",g_objname, (char *) NULL);
} else {
if (DEBUG) {
LOGE("<>");
}
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("<>");
}
}
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包,有需要的可以自行下载。
地址:
过fork分支一个子进程,子进程发现父进程死亡就重启服务或者activity。
后面有时间再写一个NDK的基础教程吧。
祝大家2016新年快乐。