转载请注明出处。
伸手党福利:compile 'com.tuzhenlei:crashhandler:1.0.1'
详情参见文档和demo:github地址
/**简单初始化*/
//CrashHandler.getInstance().init(this, BuildConfig.DEBUG);
/**个性初始化*/
CrashHandler.getInstance().init(this, BuildConfig.DEBUG, true, 0, MainActivity.class);
/**
* 参数1:this
* 参数2:是否保存日志到SD卡crash目录,建议设置为BuildConfig.DEBUG,在debug时候保存,方便调试
* 参数3:是否crash后重启APP
* 参数4:多少秒后重启app,建议设为0,因为重启采用闹钟定时任务模式,app会反应3秒钟,所以最快也是3-4秒后重启
* 参数5:重启后打开的第一个activity,建议是splashActivity
*/
/**
* 更多的设置方法
*/
/*
//自定义Toast
Toast toast = Toast.makeText(this, "自定义提示信息", Toast.LENGTH_LONG);
toast.setGravity(Gravity.BOTTOM, 0, 0);
CrashHandler.setCustomToast(toast);
//自定义提示信息
CrashHandler.setCrashTip("自定义提示信息");
//自定义APP关闭动画
CrashHandler.setCloseAnimation(android.R.anim.fade_out);
*/
复制代码
Crash相信是很多朋友开发过程经常遇到的问题。经过本人测试,Android在API21以下(也就是Android5.0以下),crash后会直接退出应用;但是在API21以上(5.0以上系统),会遵循以下原则重启:
- 包含service, 如果程序crash的时候,运行着service,那么系统会重新启动service 。
- 不包含service,只有一个Activity,那么系统不会重新启动该Activity 。
- 不包含service,但是当前栈中包含两个Activity, A–>B, 如果B crash,那么系统会重启A。
- 不包含service,但是当前栈中包含三个Activity, A–>B–>C, 如果C crash,那么系统会重启B,并且A仍然存在,即可以从重启的Back到A。
我们来看下没有进过任何处理的3种crash情况,3张图分别对应第2,第3,第4种情况
所以我们根据项目需求以及实际情况有两种解决方案,都可以避免无限crash或者是丢失必要的传递信息引起其他的crash,从而造成非常差的用户体验的情况。
- crash后不重启APP,让用户手动重启。
- crash后1秒重启APP。
首先,我们写一个crashHandler类,继承UncaughtExceptionHandler
CrashHandler implements Thread.UncaughtExceptionHandler
复制代码
然后初始化crashhandler的时候设置CrashHandler为程序的默认处理器,同时获取系统默认的UncaughtException处理器
Thread.setDefaultUncaughtExceptionHandler(this);
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
复制代码
在需要复写的uncaughtException方法中进行我们需要的处理,这里关键就是在必须要先关闭栈内所有的activity,再退出APP:
/**
* 当UncaughtException发生时会转入该函数来处理
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
boolean isHandle = handleException(ex);
if (!isHandle && mDefaultHandler != null) {
// 如果我们没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
//给Toast留出时间
Thread.sleep(2800);
} catch (InterruptedException e) {
Log.e(TAG, "uncaughtException() InterruptedException:" + e);
}
if (mIsRestartApp) {
//利用系统时钟进行重启任务
AlarmManager mgr = (AlarmManager) mApplication.getSystemService(Context.ALARM_SERVICE);
try {
Intent intent = new Intent(mApplication, mClassOfFirstActivity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent restartIntent = PendingIntent.getActivity(mApplication, 0, intent, PendingIntent.FLAG_ONE_SHOT);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + mRestartTime, restartIntent); // x秒钟后重启应用
} catch (Exception e) {
Log.e(TAG, "first class error:" + e);
}
}
mMyActivityLifecycleCallbacks.removeAllActivities();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
System.gc();
}
}
复制代码
handleException的方法主要是为了弹出Toast和收集crash信息
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(Throwable ex) {
if (!hasToast) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Looper.prepare();
Toast toast;
if (mCustomToast == null) {
toast = Toast.makeText(mApplication, mCrashTip, Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
} else {
toast = mCustomToast;
}
toast.show();
Looper.loop();
hasToast = true;
} catch (Exception e) {
Log.e(TAG, "handleException Toast error" + e);
}
}
}).start();
}
if (ex == null) {
return false;
}
if (mIsDebug) {
// 收集设备参数信息
collectDeviceInfo();
// 保存日志文件
saveCatchInfo2File(ex);
}
return true;
}
复制代码
由于我们要关闭栈内所有activity,所以要监听每个activity的生命周期,建议直接在application里面注册一个ActivityLifecycleCallbacks,实现如下:
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
private List activities = new LinkedList<>();
public static int sAnimationId = 0;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
addActivity(activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
removeActivity(activity);
}
/**
* 添加Activity
*/
public void addActivity(Activity activity) {
if (activities == null) {
activities = new LinkedList<>();
}
if (!activities.contains(activity)) {
activities.add(activity);//把当前Activity添加到集合中
}
}
/**
* 移除Activity
*/
public void removeActivity(Activity activity) {
if (activities.contains(activity)) {
activities.remove(activity);
}
if (activities.size() == 0) {
activities = null;
}
}
/**
* 销毁所有activity
*/
public void removeAllActivities() {
for (Activity activity : activities) {
if (null != activity) {
activity.finish();
activity.overridePendingTransition(0, sAnimationId);
}
}
}
}
复制代码
然后用appliction.registerActivityLifecycleCallbacks(new MyActivityLifecycleCallbacks());即可。
经过我们处理后的情况如下图所示:
这样问题就解决了(已封装好,直接依赖即可使用)。