欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Crash 监控

程序员文章站 2022-04-15 18:41:57
...

Crash(应用崩溃)是由于代码异常而导致 App 非正常退出,导致应用程序无法继续使用,所有工作都停止的现象。发生 Crash 后需要重新启动应用(有些情况会自动重启),而且不管应用在开发阶段做得多么优秀,也无法避免 Crash 发生,特别是在 Android 系统中,系统碎片化严重、各 ROM 之间的差异,使 Android 在稳定性方面需要付出更多的代价。

在应用中发生 Crash 具有以下两个特点。

非必现:一般来说 Crash 不是必现的,可能是一个很少使用的场景触发,在开发和测试阶段都没有考虑到,并且是有概率地触发。

原因多,无法系统性解决:导致 Crash 的原因有很多,有代码逻辑缺陷、系统兼容问题、硬件兼容问题,而在应用开发时较少关注到,特别是硬件和 ROM 的兼容性问题,需要特定的机型/ROM 和特定的场景才触发。

因此,要降低 Crash 发生的概率,需要 Crash 监控和发生 Crash 时的堆栈信息,开发者拿到这些信息后分析导致 Crash 的原因并修复。
在 Android 应用中发生的 Crash 有两种类型,Java 层的 Crash 和 Native 层 Crash。这两种Crash 的监控和获取堆栈信息有所不同。接下来介绍发生 Crash 时,如何监控和获取日志。

  • Java 层 层 Crash
    在 Android 中,Java 虚拟机为每个进程都设置了一个默认 UncaughtExceptionHandler,用于处理本进程中未被try catch的Exception。因此只要实现UncaughtExceptionHandler 接口,并在进程启动时调用 Thread.setDefaultUncaughtExceptionHandler (…)传入自定义的 UncaughtExceptionHandler,当出现没被 catch 的异常时,就会回调 uncaughtException(Thread thread,Throwable ex)方法,可在这里记录 crash 日志,并上报给服务器,也可以执行一些个性化的异常处理操作。

下面是一个实现 Thread.UncaughtExceptionHandler 接口的类:CrashHandler,代码如下

public class CrashHandler implements Thread.UncaughtExceptionHandler {

    private static final String TAG = "CrashHandler";
    private static final String CRASH_FILE_NAME = "crash";
    String p = "";
    private static final String CRASH_FILE_PATH = Environment.getExternalStorageDirectory().getPath() + "/log/";
    private static final String CRASH_FILE_NAME_SUFFIX = ".txt";

    private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
    private Context mContext;

    public void init(Context context) {
        mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
        this.mContext = context;
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        dumpExceptionToSDCard(e);
        if (mDefaultCrashHandler !=null){
            mDefaultCrashHandler.uncaughtException(t,e);
        }else {
            Process.killProcess(Process.myPid());
        }
    }

    private void dumpExceptionToSDCard(Throwable e) {
        File dir = new File(CRASH_FILE_PATH);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        File file = new File(CRASH_FILE_PATH + CRASH_FILE_NAME + time + CRASH_FILE_NAME_SUFFIX);
        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            pw.println(time);
            getPhoneInfo(pw);
            pw.println();
            // 导出异常的调用栈信息
            e.printStackTrace(pw);
            pw.close();
        } catch (IOException ex) {
            Log.e(TAG, ex.toString());
        }
    }

    private void getPhoneInfo(PrintWriter pw) {
        // 上报手机信息 版本 等等
    }

}

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        CrashHandler crashHandler = new CrashHandler();
        crashHandler.init(this);
    }
}

当发生 Crash 后,CrashHandler 类通过回调方法 uncaughtException,在这里可以拿到错误的信息,也可以上报更多的辅助数据去分析。而 CrashHandler 最好是在 AppApplication 中注册,这样就可以监听 Java 层的 Crash,并保存发生 Crash 的错误堆栈,或者根据需求增加一些辅助信息。

相关标签: Crash