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

来源:互联网 发布:python cst时间转换 编辑:程序博客网 时间:2024/06/11 23:46

转载请注明出处。

伸手党福利:compile ‘com.tuzhenlei:crashhandler:1.0.1’ ,详情参见文档和demo:github地址

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发生时会转入该函数来处理 */@Overridepublic 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;@Overridepublic void onActivityCreated(Activity activity, Bundle savedInstanceState) {    addActivity(activity);}@Overridepublic void onActivityStarted(Activity activity) {}@Overridepublic void onActivityResumed(Activity activity) {}@Overridepublic void onActivityPaused(Activity activity) {}@Overridepublic void onActivityStopped(Activity activity) {}@Overridepublic void onActivitySaveInstanceState(Activity activity, Bundle outState) {}@Overridepublic 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

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

原创粉丝点击