Android Crash处理 崩溃后禁止默认重启与崩溃后手动重启

转载自:https://juejin.im/post/5a321db5f265da431b6d38ff

伸手党福利: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以上系统),会遵循以下原则重启:

  1. 包含service, 如果程序crash的时候,运行着service,那么系统会重新启动service 。
  2. 不包含service,只有一个Activity,那么系统不会重新启动该Activity 。
  3. 不包含service,但是当前栈中包含两个Activity, A–>B, 如果B crash,那么系统会重启A。
  4. 不包含service,但是当前栈中包含三个Activity, A–>B–>C, 如果C crash,那么系统会重启B,并且A仍然存在,即可以从重启的Back到A。

我们来看下没有进过任何处理的3种crash情况,3张图分别对应第2,第3,第4种情况

crash at activity1.gif

crash at activity2.gif

crash at activity3.gif

所以我们根据项目需求以及实际情况有两种解决方案,都可以避免无限crash或者是丢失必要的传递信息引起其他的crash,从而造成非常差的用户体验的情况。

  1. crash后不重启APP,让用户手动重启。
  2. 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<Activity> 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());即可。

经过我们处理后的情况如下图所示:

demo after handle.gif

这样问题就解决了(已封装好,直接依赖即可使用)。

猜你喜欢

转载自blog.csdn.net/qq_35559358/article/details/82865237