由于我们做的是机器人上的软件,而机器人是24小时不间断服务的,这就要求我们的软件不能退出到系统桌面。当然最好是能够做到程序能够不卡顿,不崩溃,自己不退出。由于我们引用了很多第三方的开发包,也不能保证他们的稳定性,所以,要做到完全不崩溃也是不可能的。

退而求其次,如果崩溃了我们就要保证程序能够被拉起来,期间也看过很多保活的方案,比如service前台的方法,比如jni里写守护进程,比如接收系统广播唤醒,比如用alarmmanager唤醒等等,感觉不是效率底,就是被系统屏蔽了。经过不断筛选,我认为使用aidl进行双进程守护其实是效率很好的一个解决方案。

其实这个原理也很简单,简单的说就是创建两个service,其中一个再程序主进程,另外一个在其他进程,这两个进程通过aidl通信,一旦其中一个进程断开连接,那么就重启该服务,两个程序互相监听,就能够做到一方被杀死,另一方被启动了。当然,如果使用 adb shell force-stop packageName的方法杀死程序,肯定是不能够重启的。这种方式仅仅是为了避免ndk层崩溃,java抓不到从而不能使用java层重启应用的一种补充方式。要想做到完全不被杀死,那就太流氓了。

说了这么多,看代码吧

两个service,localservice和remoteservice

LocalService.java

  1. package guide.yunji.com.guide.processGuard;
  2.  
  3. import android.app.Application;
  4. import android.app.Service;
  5. import android.content.ComponentName;
  6. import android.content.Context;
  7. import android.content.Intent;
  8. import android.content.ServiceConnection;
  9. import android.os.IBinder;
  10. import android.os.RemoteException;
  11. import android.util.Log;
  12. import android.widget.Toast;
  13.  
  14. import guide.yunji.com.guide.MyApplication;
  15. import guide.yunji.com.guide.activity.MainActivity;
  16. import guide.yunji.com.guide.testFace.IMyAidlInterface;
  17.  
  18. public class LocalService extends Service {
  19. private static final String TAG = LocalService.class.getName();
  20. private MyBinder mBinder;
  21.  
  22. private ServiceConnection connection = new ServiceConnection() {
  23. @Override
  24. public void onServiceConnected(ComponentName name, IBinder service) {
  25. IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
  26. try {
  27. Log.e("LocalService", "connected with " + iMyAidlInterface.getServiceName());
  28. //TODO whh 本地service被拉起,检测如果mainActivity不存在则拉起
  29. if (MyApplication.getMainActivity() == null) {
  30. Intent intent = new Intent(LocalService.this.getBaseContext(), MainActivity.class);
  31. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  32. getApplication().startActivity(intent);
  33. }
  34. } catch (RemoteException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38.  
  39. @Override
  40. public void onServiceDisconnected(ComponentName name) {
  41. Toast.makeText(LocalService.this, "链接断开,重新启动 RemoteService", Toast.LENGTH_LONG).show();
  42. Log.e(TAG, "onServiceDisconnected: 链接断开,重新启动 RemoteService");
  43. startService(new Intent(LocalService.this, RemoteService.class));
  44. bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
  45. }
  46. };
  47.  
  48. public LocalService() {
  49. }
  50.  
  51. @Override
  52. public void onCreate() {
  53. super.onCreate();
  54. }
  55.  
  56. @Override
  57. public int onStartCommand(Intent intent, int flags, int startId) {
  58. Log.e(TAG, "onStartCommand: LocalService 启动");
  59. Toast.makeText(this, "LocalService 启动", Toast.LENGTH_LONG).show();
  60. startService(new Intent(LocalService.this, RemoteService.class));
  61. bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
  62. return START_STICKY;
  63. }
  64.  
  65. @Override
  66. public IBinder onBind(Intent intent) {
  67. mBinder = new MyBinder();
  68. return mBinder;
  69. }
  70.  
  71. private class MyBinder extends IMyAidlInterface.Stub {
  72.  
  73. @Override
  74. public String getServiceName() throws RemoteException {
  75. return LocalService.class.getName();
  76. }
  77.  
  78. @Override
  79. public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
  80.  
  81. }
  82. }
  83. }

RemoteService.java

  1. package guide.yunji.com.guide.processGuard;
  2.  
  3. import android.app.Service;
  4. import android.content.ComponentName;
  5. import android.content.Context;
  6. import android.content.Intent;
  7. import android.content.ServiceConnection;
  8. import android.os.IBinder;
  9. import android.os.RemoteException;
  10. import android.util.Log;
  11. import android.widget.Toast;
  12.  
  13. import guide.yunji.com.guide.testFace.IMyAidlInterface;
  14.  
  15. public class RemoteService extends Service {
  16. private static final String TAG = RemoteService.class.getName();
  17. private MyBinder mBinder;
  18.  
  19. private ServiceConnection connection = new ServiceConnection() {
  20. @Override
  21. public void onServiceConnected(ComponentName name, IBinder service) {
  22. IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
  23. try {
  24. Log.e(TAG, "connected with " + iMyAidlInterface.getServiceName());
  25. } catch (RemoteException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29.  
  30. @Override
  31. public void onServiceDisconnected(ComponentName name) {
  32. Log.e(TAG, "onServiceDisconnected: 链接断开,重新启动 LocalService");
  33. Toast.makeText(RemoteService.this, "链接断开,重新启动 LocalService", Toast.LENGTH_LONG).show();
  34. startService(new Intent(RemoteService.this, LocalService.class));
  35. bindService(new Intent(RemoteService.this, LocalService.class), connection, Context.BIND_IMPORTANT);
  36. }
  37. };
  38.  
  39. public RemoteService() {
  40. }
  41.  
  42. @Override
  43. public int onStartCommand(Intent intent, int flags, int startId) {
  44. Log.e(TAG, "onStartCommand: RemoteService 启动");
  45. Toast.makeText(this, "RemoteService 启动", Toast.LENGTH_LONG).show();
  46. bindService(new Intent(this, LocalService.class), connection, Context.BIND_IMPORTANT);
  47. return START_STICKY;
  48. }
  49.  
  50. @Override
  51. public IBinder onBind(Intent intent) {
  52. mBinder = new MyBinder();
  53. return mBinder;
  54. }
  55.  
  56. private class MyBinder extends IMyAidlInterface.Stub {
  57.  
  58. @Override
  59. public String getServiceName() throws RemoteException {
  60. return RemoteService.class.getName();
  61. }
  62.  
  63. @Override
  64. public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
  65.  
  66. }
  67. }
  68. }

注意,两个service要在不通的进程

  1. <service
  2. android:name=".processGuard.LocalService"
  3. android:enabled="true"
  4. android:exported="true" />
  5. <service
  6. android:name=".processGuard.RemoteService"
  7. android:enabled="true"
  8. android:exported="true"
  9. android:process=":RemoteProcess" />

两个service通过aidl连接,如下

  1. // IMyAidlInterface.aidl
  2. package guide.yunji.com.guide.testFace;
  3.  
  4. // Declare any non-default types here with import statements
  5.  
  6. interface IMyAidlInterface {
  7. /**
  8. * Demonstrates some basic types that you can use as parameters
  9. * and return values in AIDL.
  10. */
  11. void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
  12. double aDouble, String aString);
  13. String getServiceName();
  14. }

此外还要注意一点,程序的service初始化的时候如果在自定义的application的时候要注意多进程的问题,本来LocalService是在主进程中启动的,所以要做一下进程的判断,如下:

  1. package com.honghe.guardtest;
  2.  
  3. import android.app.ActivityManager;
  4. import android.app.Application;
  5. import android.content.Context;
  6. import android.content.Intent;
  7.  
  8. public class MyApplication extends Application {
  9. private static MainActivity mainActivity = null;
  10.  
  11. public static MainActivity getMainActivity() {
  12. return mainActivity;
  13. }
  14.  
  15. public static void setMainActivity(MainActivity activity) {
  16. mainActivity = activity;
  17. }
  18.  
  19. @Override
  20. public void onCreate() {
  21. super.onCreate();
  22. if (isMainProcess(getApplicationContext())) {
  23. startService(new Intent(this, LocalService.class));
  24. } else {
  25. return;
  26. }
  27. }
  28.  
  29. /**
  30. * 获取当前进程名
  31. */
  32. public String getCurrentProcessName(Context context) {
  33. int pid = android.os.Process.myPid();
  34. String processName = "";
  35. ActivityManager manager = (ActivityManager) context.getApplicationContext().getSystemService
  36. (Context.ACTIVITY_SERVICE);
  37. for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) {
  38. if (process.pid == pid) {
  39. processName = process.processName;
  40. }
  41. }
  42. return processName;
  43. }
  44.  
  45. public boolean isMainProcess(Context context) {
  46. /**
  47. * 是否为主进程
  48. */
  49. boolean isMainProcess;
  50. isMainProcess = context.getApplicationContext().getPackageName().equals
  51. (getCurrentProcessName(context));
  52. return isMainProcess;
  53. }
  54. }

然后LocalService重启后,可以判断是否要开启程序的主界面,上面的localService已经写了,就不多介绍了。

代码已经有了,我们怎么测试呢?

当然是伪造一个ndk的崩溃来验证程序的可行性了。

我们写一个jni,如下

写一个Jni的类

JniLoaderndk.cpp

  1. #include <string.h>
  2. #include <jni.h>
  3. #include <stdio.h>
  4.  
  5. //#include "yue_excample_hello_JniLoader.h"
  6. //按照C语言规则编译。jni依照C的规则查找函数,而不是C++,没有这一句运行时会崩溃报错:
  7. // java.lang.UnsatisfiedLinkError: Native method not found:
  8. extern "C"{
  9.  
  10. JNIEXPORT jstring JNICALL Java_com_honghe_guardtest_JniLoader_getHelloString
  11. (JNIEnv *env, jobject _this)
  12. {
  13. int m=;
  14. int n=;
  15. int j=m/n;
  16. printf("hello %d",j);
  17. Java_com_honghe_guardtest_JniLoader_getHelloString(env,_this);
  18. //return (*env)->NewStringUTF(env, "Hello world from jni)");//C语言格式,文件名应为xxx.c
  19. return env->NewStringUTF((char *)("hello whh"));//C++格式,文件名应为xxx.cpp
  20. }
  21.  
  22. }

为什么这么写,因为我本来想通过除0来制造异常,但是ndk本身并不向上层因为除0崩溃,后来无奈只好使用递归来制造崩溃了。

android.mk

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3.  
  4. # 要生成的.so库名称。java代码System.loadLibrary("firstndk");加载的就是它
  5. LOCAL_MODULE := firstndk
  6.  
  7. # C++文件
  8. LOCAL_SRC_FILES := JniLoaderndk.cpp
  9.  
  10. include $(BUILD_SHARED_LIBRARY)

application.mk

  1. # 注释掉了,不写会生成全部支持的平台。目前支持:
  2. APP_ABI := armeabi arm64-v8a armeabi-v7a mips mips64 x86 x86_64
  3. #APP_ABI := armeabi-v7a

写完了ndk后需要到jni的目录下执行一个 ndk-build 的命令,这样会在main目录下生成libs文件夹,文件夹中有目标平台的so文件,但是android默认不会读取该目录的so文件,所以我们需要在app/build.gradle中加入路径,使程序能够识别so

  1. sourceSets {
  2. main {
  3. jniLibs.srcDirs = ['src/main/libs']//默认为jniLibs
  4. }
  5. }

弄好后,就可以在安卓程序中找到ndk中的方法了。

创建调用类

JniLoader.java

  1. package com.honghe.guardtest;
  2.  
  3. public class JniLoader {
  4. static {
  5. System.loadLibrary("firstndk");
  6. }
  7.  
  8. public native String getHelloString();
  9. }

调用该方法就能够发现程序在ndk影响下崩溃了,如图

看logcat

说明旧的进程由于ndk崩溃被杀死了,但是看界面里程序已经重启了,然后还多出了一个不通pid的同名进程,如下

证明ndk崩溃后我们的软件重启成功了。

代码全部在github,如下

https://github.com/dongweiq/guardTest

我的github地址:https://github.com/dongweiq/study

欢迎关注,欢迎star o(∩_∩)o 。有什么问题请邮箱联系 dongweiqmail@gmail.com qq714094450

android双进程守护,让程序崩溃后一定可以重启的更多相关文章

  1. Android 保持Service不被Kill掉的方法--双Service守护 && Android实现双进程守护

    本文分为两个部分,第一部分为双Service守护,第二部分为双进程守护 第一部分: 一.Service简介:Java.lang.Object ↳Android.content.Context  ↳an ...

  2. Android实现双进程守护 (转)

    做过android开发的人应该都知道应用会在系统资源匮乏的情况下被系统杀死!当后台的应用被系统回收之后,如何重新恢复它呢?网上对此问题有很多的讨论.这里先总结一下网上流传的各种解决方案,看看这些办法是 ...

  3. 保持Service不被Kill掉的方法--双Service守护 && Android实现双进程守护

    本文分为两个部分,第一部分为双Service守护,第二部分为双进程守护 第一部分: 一.Service简介:Java.lang.Object ↳Android.content.Context  ↳an ...

  4. Android NDK(C++) 双进程守护

    双进程守护如果从进程管理器观察会发现新浪微博.支付宝和QQ等都有两个以上相关进程,其中一个就是守护进程,由此可以猜到这些商业级的软件都采用了双进程守护的办法. 什么是双进程守护呢?顾名思义就是两个进程 ...

  5. Android实现双进程守护

    做过android开发的人应该都知道应用会在系统资源匮乏的情况下被系统杀死!当后台的应用被系统回收之后,如何重新恢复它呢?网上对此问题有很多的讨论.这里先总结一下网上流传的各种解决方案,看看这些办法是 ...

  6. 结合程序崩溃后的core文件分析bug

    引言     在<I/O的效率比较>中,我们在修改图1程序的BUF_SIZE为8388608时,运行程序出现崩溃,如下图1:          图1. 段错误     一般而言,导致程序段 ...

  7. Window提高_3.1练习_双进程守护

    双进程守护 当打开一个进程A的时候,此进程检测是否存在进程B,如果不存在就创建进程B. 进程B的作用是检测进程A是否被关闭,如果被关闭了,就再创建一个进程A. 双进程守护A.exe代码如下: #inc ...

  8. jenkins结合supervisor进行python程序发布后的自动重启

    jenkins结合supervisor进行python程序发布后的自动重启 项目背景: 通过jenkins发布kvaccount.chinasoft.com站点的python服务端程序,业务部门同事需 ...

  9. android程序崩溃后重启

    有时候由于测试不充分或者程序潜在的问题而导致程序异常崩溃,这个是令人无法接受的,在android中怎样捕获程序的异常崩溃,然后进行一些必要的处理或重新启动 应用这个问题困恼了我很久,今天终于解决了该问 ...

随机推荐

  1. php集成开发环境搭建三种方式

    三种方式都是一键搭建php开发环境 三种方式前提都是在linux下 wamp和phpstudy就不再用了 首先打造linux开发环境,通过vagrant+vbox实现本地文件同步到虚拟机上进行同步开发 ...

  2. 分页查询——Hibernate Criteria实现一次查询取得总记录数和分页后结果集

    使用Hibernate criteria进行分页查询时,如何实现一次查询取得总记录数和分页后结果集 - bto310 - ITeye博客 https://bto310.iteye.com/blog/1 ...

  3. [STM32].NVIC嵌套中断向量的理解

    转自:http://www.21ic.com/embed/jiaocheng/sheji/201209/5634.html 一.STM32 (Cortex-M3) 中的优先级概念 STM32(Cort ...

  4. C语言结构体变量字节对齐问题总结

    结构体字节对齐 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题.从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但 ...

  5. php的类使用样例

    这个demo.差不多php的类的主要知识点都用到了. public,private关键字, namespace,use命令空间, require导入, interface复用, abstract抽象类 ...

  6. 2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)

    题目链接 传送门 题意 给你一棵无根树,要你寻找一个根节点使得在将一条边权变为\(0\)后,离树根最远的点到根节点的距离最小. 思路 本题和求树的直径很像,不过要记得的东西有点多,且状态也很多. \( ...

  7. Educational Codeforces Round 68 E. Count The Rectangles

    Educational Codeforces Round 68 E. Count The Rectangles 传送门 题意: 给出不超过\(n,n\leq 5000\)条直线,问共形成多少个矩形. ...

  8. loadrunner:传json

    loadrunner传k-v,用web_custom_request函数. init里面执行登录,根据返回获取到tokenId,action中,执行登录后的操作. 详细脚本如下: vuser_init ...

  9. nginx访问url内容过滤

    当访问的url中含有/%df时返回404 location / { if ($request_uri ~* "/%df") { # return 200 "error&q ...

  10. FitNesseRoot/ErrorLogs目录下可查看fitnesse输出日志

    调试fitnesse用例时,通过测试页面的输出信息不是很好定位问题出在哪里 这时可以在写代码过程中,增加一些输出信息,比如说java的话,可以用log4j.注意要把日志输出弄成utf-8编码,不然会中 ...