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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tuzhenlei/article/details/78800962

转载请注明出处。

伸手党福利:compile ‘com.tuzhenlei:crashhandler:1.0.1’
详情参见文档和demo:github地址
用的时候直接依赖,然后在application中init

    /**简单初始化*/
    //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/tuzhenlei/article/details/78800962
今日推荐