近期在开发过程中遇到几个问题,应用B在初始化的时候,需要通过Android板子的芯片加密验证,但是现在加密芯片不稳定,有时候能够初始化通过,有时候整个应用会直接崩溃掉,出现“程序已经停止运行“的Dialog,需要人为的点击确定,然后去打开应用。这是问题一;问题二:程序在跑的过程中,有时也会遇到因为加密芯片不稳定而造成的程序崩溃。问题三:加密芯片在初始化的过程中,不会崩溃掉,这种情况下应用没办法正常工作;但是现在我们的板子是需要挂在公交车上面,进行视频采集的,因为这种场景下是无屏幕,无声音,人是不可能进行操作。因此,必须要有一种实现方式,务必确保程序能够始终运行在后台。说的更加直白一点就是必须要有一个守护的进程,对其进行守护,那要实现这个功能,最简单的想法就是:做一个守护的APP,开启监听开机广播,然后在广播中启动一个服务,在该服务中将查看当前运行的进程,如果发现B应用还未启动,那么我们通过隐士意图的方式启动应用B。
此外,这里还有一个关键性的问题!我们的应用在崩溃的时候,android的系统会提示”运行停止“,这个时候,我们的应用进程还在后台,只有当我们点击确定的时候,这个进程才被回收掉。那么,怎么解决这个问题呢?我们需要自定义一个异常CrashHandler继承自UncaughtExceptionHandler,并Thread.setDefaultUncaughtExceptionHandler()方法将异常处理类设置到线程上。在异常的处理上,我们直接干掉这个进程,这样我们的看门狗就可以发现了。这个可以解决问题一以及问题二。但是问题三没得到解决,本来想着在遇到情况三时,直接自己杀结束自己进程,但是现在由于芯片以及库的原因不允许这么干。所以怎么办?这里我想到了给看门狗发消息的方式。我们在进入程序的时候使用AIDL技术给看门狗发一条消息msg1,在初始化成功以后,会给看门狗再发一条消息msg2。这样,当看门狗收不到消息msg2的时候就该重启这个应用B了(注意使用AIDL文件务必确保他所在的包路径一样!!!)。下面是看门狗的Manifest文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.vechicledog"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.reconova.vechicledog.activity.MainActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 看门狗服务 -->
<service android:name="com.reconova.vechicledog.service.VechicleDogService" >
<intent-filter>
<action android:name="com.reconova.VECHICLE_DOG_SERVICE" />
</intent-filter>
</service>
<!-- 开机监听 -->
<receiver android:name="com.reconova.vechicledog.receiver.MyBooCompleteReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- <!– 开机监听 –>
<receiver android:name="com.reconova.vechicledog.receiver.MyReceiver" >
<intent-filter>
<action android:name="MYRECEIVER" />
</intent-filter>
</receiver>-->
</application>
</manifest>
对于AIDL技术,使用的时候,我的理解是,他就像一个服务器,客户端给他发消息,他接收到消息以后就进行处理。下面是Service服务
package com.reconova.vechicledog.service;
import java.util.List;
import com.reconova.aidl.IVechicleControl;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.audiofx.AudioEffect.OnEnableStatusChangeListener;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
* 使用AIDL实现类似看门狗的程序
* @author GuanYangYi
*
*/
public class VechicleDogService extends Service {
private RemoteControl remoteControl = new RemoteControl();
private Context context ;
private ActivityManager manager ;
private boolean initSecondSuccess;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.v("pos","onBind");
if (context==null) {
context = getApplicationContext();
manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
watchAfterInitSuccess();
}
return remoteControl;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.v("pos","onCreate");
context = getApplicationContext();
manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
watchAfterInitSuccess();
}
/**
* 启动汽车
*/
private void startVechicele(){
if (context!=null){
Intent mainIntent = new Intent();
mainIntent.setAction(Intent.ACTION_MAIN);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mainIntent.setClassName("com.reconova.vehicle2", "com.reconova.activity.RecoFaceActivity2");
mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(mainIntent);
}
}
/**
* 初始化成功以后进行监控
*/
private void watchAfterInitSuccess() {
//开启线程开始检测
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
boolean isSafe = false;
List<RunningAppProcessInfo> infos = manager.getRunningAppProcesses();
for (RunningAppProcessInfo info:infos){
if ("com.reconova.vehicle".equals(info.processName)){
Log.v("pos","Name==="+info.processName);
isSafe = true;
continue;
}
}
if (!isSafe){
startVechicele();
}
}
}
}).start();
}
/**
* @author GuanYangYi
* 实现对远方消息的监控
*/
private final class RemoteControl extends IVechicleControl.Stub implements IVechicleControl{
@Override
public void enterInit(int msgCount) throws RemoteException {
// TODO Auto-generated method stub
Log.v("pos","enterInit");
onEnterInit();
}
@Override
public void successInit(int msgCount) throws RemoteException {
// TODO Auto-generated method stub
Log.v("pos","successInit");
initSecondSuccess = true;
}
/**
* 开启就开始检测
*/
private void onEnterInit() {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true){
long startTime = System.currentTimeMillis();
long end = System.currentTimeMillis();
while ( (end-startTime)<=5000 ){
if (initSecondSuccess){ //5秒以内收到初始化成功的消息
watchAfterInitSuccess();
initSecondSuccess = false;
return;
}else{
end = System.currentTimeMillis();
}
}
startVechicele();
return ;
}
}
}).start();
}
@Override
public void test(String msg) throws RemoteException {
// TODO Auto-generated method stub
Log.v("pos",msg);
}
}
}
package com.reconova.vechicledog.activity;
import com.reconova.vechicledog.service.VechicleDogService;
import android.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
startService(new Intent(MainActivity.this,VechicleDogService.class));
}
}
客户端:
客户端也也有一个监听开机广播,然后在广播中自己启动自己,只不过很遗憾的是,这个过程很快。快到以至于我们的远程服务都没绑定,所以消息到达不了看门狗,怎么办?延时加载?可是这可是在广播中啊,这个时候我采取的办法是直接把进程给结束掉,让看门狗看得到,让看门狗来启动!那么问题来了,为什么不让看门狗来启动应用B呢?看门狗不是自己都有启动应用B的功能吗?是的,的确是有的!但是很可惜,这个这样子的启动方式会出现”停止运行“的提示,进程没被干掉。究其根本原因,应该是代码中线程运转太快,重复start应用B的原因。以下是开机监听广播
package com.reconova.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.sax.StartElementListener;
import android.widget.Toast;
public class MyBooCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
try {
Thread.sleep(1000); //leave some time for watch dog
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}<span style="color: rgb(255, 0, 0);">
</span>
然后是客户端的MainActivity:
package com.reconova.activity;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.FeatureInfo;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.hardware.Camera;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.reconova.aidl.IVechicleControl;
import com.reconova.data.ImageStruct.ImageResult.FaceRect;
import com.reconova.jnuit.SFHCameraCallback3;
import com.reconova.model.FaceVerification;
import com.reconova.model.FrameDataQueue;
import com.reconova.model.MyQueue;
import com.reconova.model.TempFrameData;
import com.reconova.processor.EyeDetectorProcessor2;
import com.reconova.processor.FaceDetectorProcessor;
import com.reconova.processor.FaceProcessorWrapper;
import com.reconova.processor.ImageHolder;
import com.reconova.processor.NativeFaceProcessor;
import com.reconova.processor.InfoHolder.RecoParams;
import com.reconova.processor.NativeImageCapture;
import com.reconova.processor.ProcessorManager;
import com.reconova.processor.RecoProcessor;
import com.reconova.processor.RecoProcessor.ExtractResult;
import com.reconova.processor.RecoProcessor.FaceData;
import com.reconova.strategy.TiredStrategy;
import com.reconova.task.TaskQueue;
import com.reconova.task.WorkThread;
import com.reconova.ui.base.FaceCanvasView;
import com.reconova.utils.CaptureImage;
import com.reconova.utils.CrashHandler;
import com.reconova.utils.DateInfoUtils;
import com.reconova.utils.FileTool;
import com.reconova.utils.ImageHelper;
import com.reconova.utils.LogUtils;
import com.reconova.utils.MyPreviewCallBack;
import com.reconova.utils.PlaySoundPool;
import com.reconova.utils.SFHCameraCallback;
import com.reconova.vehicle2.R;
/**
* @author guluxixi
* 人脸检测、注册、识别、疲劳检测界面。
*/
/*public class RecoFaceActivity extends BaseActivity implements
Camera.PreviewCallback {*/
public class RecoFaceActivity2 extends BaseActivity implements
MyPreviewCallBack {
public static final String TAG = "RecoFaceActivity";
private SFHCameraCallback3 myCallback;
@Override
protected void onResume() {
super.onResume();
setUpParameters();
if (!isInitSuccess()) {
requestInitNativeLibTask();
}
}
/**
* 相关算法是否初始化成功
*/
private boolean isInitSuccess() {
return mEyeDetectorProcessor.isInitSuccess()
&& FaceDetectorProcessor.getInstance().isInitSuccess()
&& RecoProcessor.getInstance().isFaceInitSuccess();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//ShellUtils.execCommand("mount -o remount -o rw /system", true);
myIntent.setAction("com.reconova.VECHICLE_DOG_SERVICE");
bindService(myIntent, conn , Context.BIND_AUTO_CREATE);
CrashHandler.getInstance().init(getApplicationContext());
context = getApplicationContext();
setContentView(R.layout.activity_reco_face);
}
private void beforeEnterInit() {
// TODO Auto-generated method stub
if (mServiceControl!=null){
try {
mServiceControl.enterInit(1);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
}
}
/**
* 请求初始化算法库。
*/
private void requestInitNativeLibTask() {
InitNativeLibTask task = new InitNativeLibTask();
task.execute();
}
private class InitNativeLibTask extends AsyncTask<Object, Object, Boolean> {
ProgressDialog dialog;
@Override
public void onPreExecute() {
if (dialog == null) {
dialog = new ProgressDialog(RecoFaceActivity2.this);
dialog.setTitle("初始化资源库");
dialog.setMessage("初始化中请耐心等待");
dialog.setCancelable(false);
}
dialog.show();
}
@Override
protected Boolean doInBackground(Object... arg0) {
try {
Thread.sleep(3000);
beforeEnterInit();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
boolean eyeInit = mEyeDetectorProcessor.Init(
getApplicationContext(), fileDir);
if (!eyeInit) {
Log.e(TAG, "EyeInit Failed!");
return false;
}
boolean faceInit = RecoProcessor.getInstance().faceInit(
getApplicationContext(), fileDir);
if (!faceInit) {
Log.e(TAG, "FaceInit Failed!");
} else {
RecoProcessor.getInstance().configFaceSize(80, 512);
}
return faceInit;
}
protected void onPostExecute(Boolean result) {
dialog.dismiss();
if (result) {
onInitSuccess();
} else {
//onInitFailed();
//在这里改成初始化失败以后将自动进行初始化(针对无屏幕的情况)
requestInitNativeLibTask();
}
}
private void onInitSuccess() {
// TODO: Nothing to do now.
if (mServiceControl!=null){
try {
mServiceControl.successInit(2);
//自动启动
onVehicleStart();
realStart = true;
needToStop = false;
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//throw new NullPointerException();
}
}
private void onInitFailed() {
if (mRetryInitDialog == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(RecoFaceActivity2.this);
builder.setCancelable(false)
.setTitle("初始化失败!")
.setPositiveButton("重试", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mRetryInitDialog.dismiss();
requestInitNativeLibTask();
}
})
.setNegativeButton("退出程序", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mRetryInitDialog.dismiss();
RecoFaceActivity2.this.finish();
}
});
mRetryInitDialog = builder.create();
}
mRetryInitDialog.show();
}
}
/*AIDL实现看门狗的实现方式*/
private MyServiceConnection conn = new MyServiceConnection();
private IVechicleControl mServiceControl;
private Intent myIntent = new Intent();
private final class MyServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mServiceControl = IVechicleControl.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
getApplicationContext().stopService(myIntent);
}
}
}
整理的比较乱,希望能帮助到大家!