分享

Android 双进程守护尝试与分析

 风雪夜归人_95 2015-03-26
       最近在做一个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_MODULELOCAL_SRC_FILES两处即可。如果你还有添加Log打印函数,还要在这里添加 “LOCAL_LDLIBS := -lm -llog”。

下面一张图来说明整体的文件结构分布:
 其中com_example_dameonservice_Watcher.c和com_example_dameonservice_Watcher.cpp内容相同。process.cpp定义一些辅助类。

实验结果:
       这当然是大家最关心的。测试的手机选用的小米,感觉 小米在这一块的优化还是很不错的,所以用它来试试。最终的测试结果是:被杀死的服务概率性地可以重启成功,且失败的概率更大。通过Log分析,不能重启的时候是因为子进程也死掉了。截止到笔者写下这篇文章,还没有抓住其中的规律。一键清理和子进程的被杀死没有绝对的对应关系。而且即使是在App运行的时候,也会发现子进程会被杀死,然后又被父进程重启。子进程被杀死是重启失败的主要原因。但现在的现象无法确定子进程被杀死的确切原因,有一种可能是被系统杀死了,但这样的不确定性太大,对效果也不能有很好的保证。
      虽然没有完美解决问题,但至少比前面的办法强很多,至少它也重启成功过。这个方法感觉继续优化一下还是可以做好的。




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多