最近在做一个Android的项目,其包含一个消息推送的后台服务。由于该服务可能会有重要的信息推送,因此并不希望当APP程序退出、APP程序被一键清理、APP被强制停止等用户操作发生时,这个后台服务也随之被杀死。这个问题也就是所谓的“内存永驻”。关于这个问题,网上有很多说法,如调用startforehand函数以提高service的优先级、在service中创建一个不能被删掉的notification(或者产生一个其他的与用户界面交互的UI控件)、在service的onDestroy函数中重启这个服务、修改onstartcommand函数的返回值等等。这些方法,笔者都一一试过,但都没有效果。但是,我们可以看到市面上也确实存在一些App在一定的时间后可以自动重启,说明仍然是存在方法可以完成这项任务的。笔者在网上看到了这篇文章 http:///a/jingxuanboke/2014/0622/354671.html,觉得还是有些道理的。于是照着这个原理去做了。 这篇文章中介绍的方法涉及到Android的JNI编程,主要思想就是通过调用native函数创建一个子进程。父子进程相互监听,若子进程死去,父进程妥善处理后重新创建新的子进程;若父进程死去,子进程使用AM命令重启父进程。这种思想唯一的缺陷就是如何保证父子进程不被同时杀死的情况。子进程能不能被杀死,只能用实验来证明。 首先笔者按照文章介绍的,整理了代码,并将相关代码植入到自己的项目中。 步骤1)编写Watcher类。它为上面的Java程序调用提供必要的接口,声明需要native语言实现的的具体函数。native语言主要是指C/C++语言。上层的Java程序只需要创建一个Watcher类并调用它的createAppMonitor(String userId)函数即可。 public class Watcher { private static final String PACKET = "com.example.dameonservice"; private String mMonitoredService = "com.example.mqtt.MQTTSubscribeService"; private volatile boolean bHeartBreak = false; private Context mContext; private boolean mRunning = true; public void createAppMonitor(String userId) { if(!createWatcher(userId)) { Log.e("Watcher", "<<Monitor created failed>>"); } } public Watcher(Context context) { mContext = context; } /*创建一个监视子进程 *userId 当前进程的用户ID,子进程重启当前进程时需要用到当前进程的用户ID *return 若子进程创建成功返回TRUE,否则返回FALSE */ private native boolean createWatcher(String userId); /* 让当前进程连接到监视进程 * return 连接成功返回TRUE,否则返回FALSE */ private native boolean connectToMonitor(); /*向监视进程发送任意信息 * msg 发给monitor的信息 * return 实际发送的字节数 */ private native int sendMsgToMonitor(String msg); static { System.loadLibrary("monitor"); //这里要和后面的Android.mk中模块名对应 } } |
2)编译上面的文件会在bin/classes 目录下生成相对应的Watcher.class文件,通过DOs界面进入该bin/classes 目录下,通过javah命令生成C/C++对应的头文件。 “javah 包名+类名” 得到以下头文件:
#include <jni.h> /* Header for class com_example_dameonservice_Watcher */
#ifndef _Included_com_example_dameonservice_Watcher #define _Included_com_example_dameonservice_Watcher #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_dameonservice_Watcher * Method: createWatcher * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_com_example_dameonservice_Watcher_createWatcher (JNIEnv *, jobject, jstring);
/* * Class: com_example_dameonservice_Watcher * Method: connectToMonitor * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_com_example_dameonservice_Watcher_connectToMonitor (JNIEnv *, jobject);
/* * Class: com_example_dameonservice_Watcher * Method: sendMsgToMonitor * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_com_example_dameonservice_Watcher_sendMsgToMonitor (JNIEnv *, jobject, jstring);
#ifdef __cplusplus } #endif #endif
|
3)创建JNI文件夹,将得到的头文件移到该文件夹下,继续在该文件夹下创建与上面得到的头文件同名的C/C++文件,然后实现头文件中提到的方法。(具体实现太多,这里就不再贴出来了)
4)添加Android.mk文件。这个文件的格式基本是统一的。只需要修改LOCAL_MODULE和LOCAL_SRC_FILES两处即可。如果你还有添加Log打印函数,还要在这里添加 “LOCAL_LDLIBS := -lm -llog”。
下面一张图来说明整体的文件结构分布:
其中com_example_dameonservice_Watcher.c和com_example_dameonservice_Watcher.cpp内容相同。process.cpp定义一些辅助类。
实验结果: 这当然是大家最关心的。测试的手机选用的小米,感觉 小米在这一块的优化还是很不错的,所以用它来试试。最终的测试结果是:被杀死的服务概率性地可以重启成功,且失败的概率更大。通过Log分析,不能重启的时候是因为子进程也死掉了。截止到笔者写下这篇文章,还没有抓住其中的规律。一键清理和子进程的被杀死没有绝对的对应关系。而且即使是在App运行的时候,也会发现子进程会被杀死,然后又被父进程重启。子进程被杀死是重启失败的主要原因。但现在的现象无法确定子进程被杀死的确切原因,有一种可能是被系统杀死了,但这样的不确定性太大,对效果也不能有很好的保证。 虽然没有完美解决问题,但至少比前面的办法强很多,至少它也重启成功过。这个方法感觉继续优化一下还是可以做好的。
|