android双进程守护,让程序崩溃后一定可以重启
由于我们做的是机器人上的软件,而机器人是24小时不间断服务的,这就要求我们的软件不能退出到系统桌面。当然最好是能够做到程序能够不卡顿,不崩溃,自己不退出。由于我们引用了很多第三方的开发包,也不能保证他们的稳定性,所以,要做到完全不崩溃也是不可能的。
退而求其次,如果崩溃了我们就要保证程序能够被拉起来,期间也看过很多保活的方案,比如service前台的方法,比如jni里写守护进程,比如接收系统广播唤醒,比如用alarmmanager唤醒等等,感觉不是效率底,就是被系统屏蔽了。经过不断筛选,我认为使用aidl进行双进程守护其实是效率很好的一个解决方案。
其实这个原理也很简单,简单的说就是创建两个service,其中一个再程序主进程,另外一个在其他进程,这两个进程通过aidl通信,一旦其中一个进程断开连接,那么就重启该服务,两个程序互相监听,就能够做到一方被杀死,另一方被启动了。当然,如果使用 adb shell force-stop packageName的方法杀死程序,肯定是不能够重启的。这种方式仅仅是为了避免ndk层崩溃,java抓不到从而不能使用java层重启应用的一种补充方式。要想做到完全不被杀死,那就太流氓了。
说了这么多,看代码吧
两个service,localservice和remoteservice
LocalService.java
- package guide.yunji.com.guide.processGuard;
- import android.app.Application;
- import android.app.Service;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.util.Log;
- import android.widget.Toast;
- import guide.yunji.com.guide.MyApplication;
- import guide.yunji.com.guide.activity.MainActivity;
- import guide.yunji.com.guide.testFace.IMyAidlInterface;
- public class LocalService extends Service {
- private static final String TAG = LocalService.class.getName();
- private MyBinder mBinder;
- private ServiceConnection connection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
- try {
- Log.e("LocalService", "connected with " + iMyAidlInterface.getServiceName());
- //TODO whh 本地service被拉起,检测如果mainActivity不存在则拉起
- if (MyApplication.getMainActivity() == null) {
- Intent intent = new Intent(LocalService.this.getBaseContext(), MainActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getApplication().startActivity(intent);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Toast.makeText(LocalService.this, "链接断开,重新启动 RemoteService", Toast.LENGTH_LONG).show();
- Log.e(TAG, "onServiceDisconnected: 链接断开,重新启动 RemoteService");
- startService(new Intent(LocalService.this, RemoteService.class));
- bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
- }
- };
- public LocalService() {
- }
- @Override
- public void onCreate() {
- super.onCreate();
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.e(TAG, "onStartCommand: LocalService 启动");
- Toast.makeText(this, "LocalService 启动", Toast.LENGTH_LONG).show();
- startService(new Intent(LocalService.this, RemoteService.class));
- bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
- return START_STICKY;
- }
- @Override
- public IBinder onBind(Intent intent) {
- mBinder = new MyBinder();
- return mBinder;
- }
- private class MyBinder extends IMyAidlInterface.Stub {
- @Override
- public String getServiceName() throws RemoteException {
- return LocalService.class.getName();
- }
- @Override
- public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
- }
- }
- }
RemoteService.java
- package guide.yunji.com.guide.processGuard;
- import android.app.Service;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.util.Log;
- import android.widget.Toast;
- import guide.yunji.com.guide.testFace.IMyAidlInterface;
- public class RemoteService extends Service {
- private static final String TAG = RemoteService.class.getName();
- private MyBinder mBinder;
- private ServiceConnection connection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
- try {
- Log.e(TAG, "connected with " + iMyAidlInterface.getServiceName());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.e(TAG, "onServiceDisconnected: 链接断开,重新启动 LocalService");
- Toast.makeText(RemoteService.this, "链接断开,重新启动 LocalService", Toast.LENGTH_LONG).show();
- startService(new Intent(RemoteService.this, LocalService.class));
- bindService(new Intent(RemoteService.this, LocalService.class), connection, Context.BIND_IMPORTANT);
- }
- };
- public RemoteService() {
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.e(TAG, "onStartCommand: RemoteService 启动");
- Toast.makeText(this, "RemoteService 启动", Toast.LENGTH_LONG).show();
- bindService(new Intent(this, LocalService.class), connection, Context.BIND_IMPORTANT);
- return START_STICKY;
- }
- @Override
- public IBinder onBind(Intent intent) {
- mBinder = new MyBinder();
- return mBinder;
- }
- private class MyBinder extends IMyAidlInterface.Stub {
- @Override
- public String getServiceName() throws RemoteException {
- return RemoteService.class.getName();
- }
- @Override
- public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
- }
- }
- }
注意,两个service要在不通的进程
- <service
- android:name=".processGuard.LocalService"
- android:enabled="true"
- android:exported="true" />
- <service
- android:name=".processGuard.RemoteService"
- android:enabled="true"
- android:exported="true"
- android:process=":RemoteProcess" />
两个service通过aidl连接,如下
- // IMyAidlInterface.aidl
- package guide.yunji.com.guide.testFace;
- // Declare any non-default types here with import statements
- interface IMyAidlInterface {
- /**
- * Demonstrates some basic types that you can use as parameters
- * and return values in AIDL.
- */
- void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
- double aDouble, String aString);
- String getServiceName();
- }
此外还要注意一点,程序的service初始化的时候如果在自定义的application的时候要注意多进程的问题,本来LocalService是在主进程中启动的,所以要做一下进程的判断,如下:
- package com.honghe.guardtest;
- import android.app.ActivityManager;
- import android.app.Application;
- import android.content.Context;
- import android.content.Intent;
- public class MyApplication extends Application {
- private static MainActivity mainActivity = null;
- public static MainActivity getMainActivity() {
- return mainActivity;
- }
- public static void setMainActivity(MainActivity activity) {
- mainActivity = activity;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- if (isMainProcess(getApplicationContext())) {
- startService(new Intent(this, LocalService.class));
- } else {
- return;
- }
- }
- /**
- * 获取当前进程名
- */
- public String getCurrentProcessName(Context context) {
- int pid = android.os.Process.myPid();
- String processName = "";
- ActivityManager manager = (ActivityManager) context.getApplicationContext().getSystemService
- (Context.ACTIVITY_SERVICE);
- for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) {
- if (process.pid == pid) {
- processName = process.processName;
- }
- }
- return processName;
- }
- public boolean isMainProcess(Context context) {
- /**
- * 是否为主进程
- */
- boolean isMainProcess;
- isMainProcess = context.getApplicationContext().getPackageName().equals
- (getCurrentProcessName(context));
- return isMainProcess;
- }
- }
然后LocalService重启后,可以判断是否要开启程序的主界面,上面的localService已经写了,就不多介绍了。
代码已经有了,我们怎么测试呢?
当然是伪造一个ndk的崩溃来验证程序的可行性了。
我们写一个jni,如下
写一个Jni的类
JniLoaderndk.cpp
- #include <string.h>
- #include <jni.h>
- #include <stdio.h>
- //#include "yue_excample_hello_JniLoader.h"
- //按照C语言规则编译。jni依照C的规则查找函数,而不是C++,没有这一句运行时会崩溃报错:
- // java.lang.UnsatisfiedLinkError: Native method not found:
- extern "C"{
- JNIEXPORT jstring JNICALL Java_com_honghe_guardtest_JniLoader_getHelloString
- (JNIEnv *env, jobject _this)
- {
- int m=;
- int n=;
- int j=m/n;
- printf("hello %d",j);
- Java_com_honghe_guardtest_JniLoader_getHelloString(env,_this);
- //return (*env)->NewStringUTF(env, "Hello world from jni)");//C语言格式,文件名应为xxx.c
- return env->NewStringUTF((char *)("hello whh"));//C++格式,文件名应为xxx.cpp
- }
- }
为什么这么写,因为我本来想通过除0来制造异常,但是ndk本身并不向上层因为除0崩溃,后来无奈只好使用递归来制造崩溃了。
android.mk
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- # 要生成的.so库名称。java代码System.loadLibrary("firstndk");加载的就是它
- LOCAL_MODULE := firstndk
- # C++文件
- LOCAL_SRC_FILES := JniLoaderndk.cpp
- include $(BUILD_SHARED_LIBRARY)
application.mk
- # 注释掉了,不写会生成全部支持的平台。目前支持:
- APP_ABI := armeabi arm64-v8a armeabi-v7a mips mips64 x86 x86_64
- #APP_ABI := armeabi-v7a
写完了ndk后需要到jni的目录下执行一个 ndk-build 的命令,这样会在main目录下生成libs文件夹,文件夹中有目标平台的so文件,但是android默认不会读取该目录的so文件,所以我们需要在app/build.gradle中加入路径,使程序能够识别so
- sourceSets {
- main {
- jniLibs.srcDirs = ['src/main/libs']//默认为jniLibs
- }
- }
弄好后,就可以在安卓程序中找到ndk中的方法了。
创建调用类
JniLoader.java
- package com.honghe.guardtest;
- public class JniLoader {
- static {
- System.loadLibrary("firstndk");
- }
- public native String getHelloString();
- }
调用该方法就能够发现程序在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双进程守护,让程序崩溃后一定可以重启的更多相关文章
- Android 保持Service不被Kill掉的方法--双Service守护 && Android实现双进程守护
本文分为两个部分,第一部分为双Service守护,第二部分为双进程守护 第一部分: 一.Service简介:Java.lang.Object ↳Android.content.Context ↳an ...
- Android实现双进程守护 (转)
做过android开发的人应该都知道应用会在系统资源匮乏的情况下被系统杀死!当后台的应用被系统回收之后,如何重新恢复它呢?网上对此问题有很多的讨论.这里先总结一下网上流传的各种解决方案,看看这些办法是 ...
- 保持Service不被Kill掉的方法--双Service守护 && Android实现双进程守护
本文分为两个部分,第一部分为双Service守护,第二部分为双进程守护 第一部分: 一.Service简介:Java.lang.Object ↳Android.content.Context ↳an ...
- Android NDK(C++) 双进程守护
双进程守护如果从进程管理器观察会发现新浪微博.支付宝和QQ等都有两个以上相关进程,其中一个就是守护进程,由此可以猜到这些商业级的软件都采用了双进程守护的办法. 什么是双进程守护呢?顾名思义就是两个进程 ...
- Android实现双进程守护
做过android开发的人应该都知道应用会在系统资源匮乏的情况下被系统杀死!当后台的应用被系统回收之后,如何重新恢复它呢?网上对此问题有很多的讨论.这里先总结一下网上流传的各种解决方案,看看这些办法是 ...
- 结合程序崩溃后的core文件分析bug
引言 在<I/O的效率比较>中,我们在修改图1程序的BUF_SIZE为8388608时,运行程序出现崩溃,如下图1: 图1. 段错误 一般而言,导致程序段 ...
- Window提高_3.1练习_双进程守护
双进程守护 当打开一个进程A的时候,此进程检测是否存在进程B,如果不存在就创建进程B. 进程B的作用是检测进程A是否被关闭,如果被关闭了,就再创建一个进程A. 双进程守护A.exe代码如下: #inc ...
- jenkins结合supervisor进行python程序发布后的自动重启
jenkins结合supervisor进行python程序发布后的自动重启 项目背景: 通过jenkins发布kvaccount.chinasoft.com站点的python服务端程序,业务部门同事需 ...
- android程序崩溃后重启
有时候由于测试不充分或者程序潜在的问题而导致程序异常崩溃,这个是令人无法接受的,在android中怎样捕获程序的异常崩溃,然后进行一些必要的处理或重新启动 应用这个问题困恼了我很久,今天终于解决了该问 ...
随机推荐
- php集成开发环境搭建三种方式
三种方式都是一键搭建php开发环境 三种方式前提都是在linux下 wamp和phpstudy就不再用了 首先打造linux开发环境,通过vagrant+vbox实现本地文件同步到虚拟机上进行同步开发 ...
- 分页查询——Hibernate Criteria实现一次查询取得总记录数和分页后结果集
使用Hibernate criteria进行分页查询时,如何实现一次查询取得总记录数和分页后结果集 - bto310 - ITeye博客 https://bto310.iteye.com/blog/1 ...
- [STM32].NVIC嵌套中断向量的理解
转自:http://www.21ic.com/embed/jiaocheng/sheji/201209/5634.html 一.STM32 (Cortex-M3) 中的优先级概念 STM32(Cort ...
- C语言结构体变量字节对齐问题总结
结构体字节对齐 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题.从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但 ...
- php的类使用样例
这个demo.差不多php的类的主要知识点都用到了. public,private关键字, namespace,use命令空间, require导入, interface复用, abstract抽象类 ...
- 2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)
题目链接 传送门 题意 给你一棵无根树,要你寻找一个根节点使得在将一条边权变为\(0\)后,离树根最远的点到根节点的距离最小. 思路 本题和求树的直径很像,不过要记得的东西有点多,且状态也很多. \( ...
- Educational Codeforces Round 68 E. Count The Rectangles
Educational Codeforces Round 68 E. Count The Rectangles 传送门 题意: 给出不超过\(n,n\leq 5000\)条直线,问共形成多少个矩形. ...
- loadrunner:传json
loadrunner传k-v,用web_custom_request函数. init里面执行登录,根据返回获取到tokenId,action中,执行登录后的操作. 详细脚本如下: vuser_init ...
- nginx访问url内容过滤
当访问的url中含有/%df时返回404 location / { if ($request_uri ~* "/%df") { # return 200 "error&q ...
- FitNesseRoot/ErrorLogs目录下可查看fitnesse输出日志
调试fitnesse用例时,通过测试页面的输出信息不是很好定位问题出在哪里 这时可以在写代码过程中,增加一些输出信息,比如说java的话,可以用log4j.注意要把日志输出弄成utf-8编码,不然会中 ...