Android Crash 监控

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 的错误堆栈,或者根据需求增加一些辅助信息。

发布了119 篇原创文章 · 获赞 28 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ldxlz224/article/details/101208058