由于我们做的是机器人上的软件,而机器人是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双进程守护,让程序崩溃后一定可以重启的更多相关文章

  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. Java黑科技之源:JVMTI完全解读

    Java生态中有一些非常规的技术,它们能达到一些特别的效果.这些技术的实现原理不去深究的话一般并不是广为人知.这种技术通常被称为黑科技.而这些黑科技中的绝大部分底层都是通过JVMTI实现的. 形象地说 ...

  2. 将exe和dll打包为一个exe文件

    通过Nuget安装Costura.Fody 公司项目需要支持.NET 4.0(使用VS2017),Costura.Fody库从3.2.1开始支持4.0,但是不能装最新的Fody,否则会提示: Fody ...

  3. echart绘制GDP数据

    {% extends "base.html" %} {% block self_head_css_js %} {% endblock %} {% block main_conten ...

  4. Hibernate 5.x 生成 SessionFactory 源码跟踪分析

    我们要使用 Hibernate 的功能,首先需要读取 Hibernate 的配置文件,根据配置启动 Hibernate ,然后创建 SessionFactory. 创建 SessionFactory ...

  5. angular8中 如何引入两个echarts版本

    下载echarts2和echarts4的源码,然后在index.html中 1.先引入echarts4版本 2.然后引入echarts2版本 并且将echarts2源码中所有echarts 替换为ec ...

  6. CentOS6.7搭建部署DHCP服务 (详解主配置文件)

    DHCP服务 dhcp:动态主机配置协议.从bootp演变而来,引进了租约.续租功能,成为了现在的DHCP. 需要就分配,不需要就回收. 工作过程: 1.当获得地址是,有租约期限,当你关机时,IP地址 ...

  7. 个人作业第五次——Alpha项目测试

    这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/2019autumnsystemanalysisanddesign/ 这个作业的要求在哪里 https:/ ...

  8. java 的任意进制间转换(很方便)

    import java.util.Scanner; public class Main{ public static void main(String[] args) { Scanner sc = n ...

  9. 目标检测论文解读4——Faster R-CNN

    背景 Fast R-CNN中的region proposal阶段所采用的SS算法成为了检测网络的速度瓶颈,本文是在Fast R-CNN基础上采用RPN(Region Proposal Networks ...

  10. reactnative 笔记

    1.<FlatList/> _renderItem = ({item})=>{ return <View style={[styles.part4Row]}> <T ...