安卓开发笔记仓库Android开发

如何优雅的退出应用和处理崩溃异常自动重启

2017-07-21  本文已影响3042人  nanchen2251

写在前面

这是最近一些朋友问我的问题,我把它整理成了一个库,供大家享用,GitHub 地址:https://github.com/nanchen2251/AppManager

从四个应用场景说起

退出应用的几种方式

Andorid 退出应用的方式很多,常见的也就下面四种。

四种方式的代码也就不多提,需要的自己去 Android:销毁所有的 Activity 退出应用程序几种方式

莫名其妙重启?

上面的场景中说到了,再部分手机上会出现崩溃后自动重启的情况,这让我们很不好控制。经本人测试,在 Android 的 API 21 ( Android 5.0 ) 以下,Crash 会直接退出应用,但是在 API 21 ( Android 5.0 ) 以上,系统会遵循以下原则进行重启:

在这样的情况下,我们或许会有两种需求:

怎么办

翻看 API 我们发现,Java 中存在一个 UncaughtExceotionHandler 的接口,而在 Android 中我们沿用了它,我们可以采用这个接口实现我们想要的功能。
(为了方便,我把它做成了库,传送门:https://github.com/nanchen2251/AppManager

讲一些核心

CrashApplication

首先是我们的 CrashApplication 类,因为我们崩溃的时候需要结束程序后再重启,所以我们需要退出应用,这里我们采用上面的第三种方式。

public class CrashApplication extends Application {
    private List<Activity> mActivityList;


    @Override
    public void onCreate() {
        super.onCreate();
        mActivityList = new ArrayList<>();
    }

    /**
     * 添加单个Activity
     */
    public void addActivity(Activity activity) {
        // 为了避免重复添加,需要判断当前集合是否满足不存在该Activity
        if (!mActivityList.contains(activity)) {
            mActivityList.add(activity); // 把当前Activity添加到集合中
        }
    }

    /**
     * 销毁单个Activity
     */
    public void removeActivity(Activity activity) {
        // 判断当前集合是否存在该Activity
        if (mActivityList.contains(activity)) {
            mActivityList.remove(activity); // 从集合中移除
            if (activity != null){
                activity.finish(); // 销毁当前Activity
            }
        }
    }

    /**
     * 销毁所有的Activity
     */
    public void removeAllActivity() {
        // 通过循环,把集合中的所有Activity销毁
        for (Activity activity : mActivityList) {
            if (activity != null){
                activity.finish();
            }
        }
        //杀死该应用进程
        android.os.Process.killProcess(android.os.Process.myPid());
    }

}

UncaughtExceptionHandlerImpl

我们当然少不了新建一个 UncaughtExceptionHandlerImpl 类去实现我们的 UncaughtExceptionHandler 接口,它必须实现我们的 uncaughtException(thread, throwable) 方法,我们接下来可以在这中间作文章。需要特别注意的是:重启必须清除堆栈内的 Activity。

/**
     * 当 UncaughtException 发生时会转入该函数来处理
     */
    @SuppressWarnings("WrongConstant")
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }
            if (mIsRestartApp) { // 如果需要重启
                Intent intent = new Intent(mContext.getApplicationContext(), mRestartActivity);
                AlarmManager mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
                //重启应用,得使用PendingIntent
                PendingIntent restartIntent = PendingIntent.getActivity(
                        mContext.getApplicationContext(), 0, intent,
                        Intent.FLAG_ACTIVITY_NEW_TASK);
                mAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + mRestartTime,
                        restartIntent); // 重启应用
            }
            // 结束应用
            ((CrashApplication) mContext.getApplicationContext()).removeAllActivity();
        }
    }

我们的 handleException(throwable) 方法用于弹出 Toast 和收集 Crash 信息。

  /**
     * 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回 false
     */
    private boolean handleException(final Throwable ex) {
        if (ex == null) {
            return false;
        }

        // 使用 Toast 来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, getTips(ex), Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();


        //  如果用户不赋予外部存储卡的写权限导致的崩溃,会造成循环崩溃
//        if (mIsDebug) {
//            // 收集设备参数信息
//            collectDeviceInfo(mContext);
//            // 保存日志文件
//            saveCrashInfo2File(ex);
//        }
        return true;
    }

封装好的使用

1、添加依赖

Step 1. Add it in your root build.gradle at the end of repositories:
allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }
Step 2. Add the dependency
dependencies {
            compile 'com.github.nanchen2251:AppManager:1.0.1'
    }

2、在需要使用的地方使用

// 设置崩溃后自动重启 APP
UncaughtExceptionHandlerImpl.getInstance().init(this, BuildConfig.DEBUG, true, 0, MainActivity.class);

3、你也可以禁止重启

// 禁止重启
UncaughtExceptionHandlerImpl.getInstance().init(this,BuildConfig.DEBUG);

效果图

[图片上传失败...(image-ad15ca-1509694874814)]

做不完的开源,写不完的矫情。欢迎扫描下方二维码或者公众号搜索「nanchen」关注我的微信公众号,目前多运营 Android ,尽自己所能为你提升。如果你喜欢,为我点赞分享吧~


nanchen
上一篇 下一篇

猜你喜欢

热点阅读