美文网首页
xCrash 捕获ANR异常

xCrash 捕获ANR异常

作者: 思考者_小徐 | 来源:发表于2022-01-06 16:02 被阅读0次

    转自:https://blog.csdn.net/cxmfzu/article/details/103508028

    android ANR 异常的英文全称是 “Application Not Responding”,中文意思即为应用无响应。
    在介绍xCrash捕获ANR异常时,先简单介绍一下ANR异常,以及产生ANR异常的原因,以及ANR常用的处理方式。

    ANR异常产生的类型
    KeyDispatchTimeout。UI主线程对于输入事件,即Inputdispatch事件超过5S没有处理产生ANR。
    BroadcastTimeout。在广播接收器BroadcastReceiver的onReceive在一定的时间内没有执行完程序就会发生ANR异常。如果广播接收器所在的进程是前台进程,超时时间为10S;如果广播接收器所在的进程是后台进程,超时时间为60S。
    ServiceTimeout。前台的server的各个生命周期函数包括(onCreate,onStart,onBind)在20S内没有处理完成,发生ANR异常。后台的server的各个生命周期函数包括(onCreate,onStart,onBind)在200S内没有处理完成,发生ANR异常。
    ContentProviderTimeout。ContentProvider 在10S内没有处理完成发生ANR。
    ANR异常产生的原因
    在主线程执行耗时的操作,常见的耗时操作有IO操作,网络访问,大量数据的读写。
    多线程死锁,造成主线程被阻塞。
    service binder的连接达到上线无法和和System Server通信
    System Server中WatchDog出现ANR
    系统资源已耗尽(管道、CPU、IO)
    ANR异常处理。
    android系统发生ANR异常是,logcat会输出一条日志,通过日志可以确定,android 发生ANR异常时,会将日志写入/data/anr/traces.txt。因此可以猜想到应用程序可以监控/data/anr/文件是否有写入,即可判断是否发生ANR异常。如何监控文件夹,android提供了一个类“FileObserver”,可以监控文件。但是通过这种方式监控是否发生ANR异常,对android的版本有要求,API版本必须小于21。xCrash对应API版本小于21的也是采用此种方式,腾讯提供的Buly也是采用该种方式。本文章通过对xcrash的源码分析,简要分析一下此种方式监控ANR异常。

    12-12 14:51:19.705 19983-19992/com.exam.simple I/art: Wrote stack traces to '/data/anr/traces.txt'
    1
    xCrash捕获ANR异常
    xCrash是爱奇艺开源的在android平台上面捕获异常的开源库。xCrash能为安卓 APP提供捕获Java崩溃异常,native崩溃异常和ANR异常。

    xCrash 能在 App 进程崩溃或 ANR 时,在你指定的目录中生成一个 tombstone 文件(格式与安卓系统的 tombstone 文件类似)。

    xCrash项目地址:
    github:https://github.com/iqiyi/xCrash
    gitee:https://gitee.com/caikelun/xCrash

    xCrash捕获ANR异常初始化
    xCrash初始化接口,如果不设置参数InitParameters ,采用默认参数,xCrash库默认支持捕获ANR异常。

    public static int init(Context ctx) {
    return init(ctx, null);
    }

    public static synchronized int init(Context ctx, InitParameters params){
    ...
    // 允许捕获anr异常,且系统版小于21,初始化Anr异常处理器
    if (params.enableAnrHandler && Build.VERSION.SDK_INT < 21) {
    AnrHandler.getInstance().initialize(
    ctx,// context上下文
    pid,// 进程PID
    processName,// 进程名
    appId,
    params.appVersion,// app应用版本
    params.logDir,// 日志输出文件夹
    // 以下为可配置参数
    params.anrCheckProcessState,
    params.anrLogcatSystemLines,
    params.anrLogcatEventsLines,
    params.anrLogcatMainLines,
    params.anrDumpFds,
    params.anrCallback);
    }
    }

    xCrash 也支持自定义参数来设置监控ANR异常,与ANR异常相关的属性和接口如下。

    public final class XCrash {
    ...
    public static class InitParameters{
    ...
    //anr
    // anr异常处理器,默认为true,如果为false不捕获anr异常
    boolean enableAnrHandler = true;
    // 是否抛出anr异常。默认为true
    boolean anrRethrow = true;
    // 是否设置anr的状态标志给进程状态(具体参见源码中的注释)
    boolean anrCheckProcessState = true;
    // anr日志最大保留文件数量
    int anrLogCountMax = 10;
    // 执行命令 logcat -b system 输出的日志行数
    int anrLogcatSystemLines = 50;
    // 执行命令 logcat -b event 输出的日志行数
    int anrLogcatEventsLines = 50;
    // 执行命令 logcat -b maint输出的日志行数
    int anrLogcatMainLines = 200;
    // 是否输出app进程的下打开的文件描述符
    boolean anrDumpFds = true;
    // 发生anr异常的应用回调
    ICrashCallback anrCallback = null;
    }
    }

    初始化xCrash的ANR异常代码。代码中只展示ANR异常配置

    XCrash.init(this, new XCrash.InitParameters()
    .enableAnrCrashHandler() //开启ANR异常捕获;捕获disableAnrCrashHandler()
    .setAnrCheckProcessState(true) //是否设置anr的状态标志给进程状态
    .setAnrRethrow(true) // 是否抛出anr异常。默认为true
    .setAnrLogCountMax(100)
    .setAnrLogcatSystemLines(100)
    .setAnrLogcatEventsLines(100)
    .setAnrLogcatMainLines(100)
    .setAnrDumpFds(true)
    .setAnrCallback(callback)

    ANR异常处理器
    类AnrHandler即为ANR的异常处理器。AnrHandler 为单例模式源码如下。

    class AnrHandler {
    private static final AnrHandler instance = new AnrHandler();
    private AnrHandler() {
    }

    static AnrHandler getInstance() {
        return instance;
    }
    

    }

    AnrHandler 的核心代码为initialize函数接口中的FileObserver。源码如下

    private FileObserver fileObserver = null;
    void initialize(....){
    // 实例化FileObserver ,监控路径"/data/anr/",监听文件被写入
    fileObserver = new FileObserver("/data/anr/", CLOSE_WRITE) {
    public void onEvent(int event, String path) {
    try {
    if (path != null) {
    String filepath = "/data/anr/" + path;
    // 写入的文件是否有关键字 “trace”
    if (filepath.contains("trace")) {
    // 处理anr异常
    handleAnr(filepath);
    }
    }
    } catch (Exception e) {
    XCrash.getLogger().e(Util.TAG, "AnrHandler fileObserver onEvent failed", e);
    }
    }
    };

        try {
            // 启动FileObserver 监控
            fileObserver.startWatching();
        } catch (Exception e) {
            fileObserver = null;
            XCrash.getLogger().e(Util.TAG, "AnrHandler fileObserver startWatching failed", e);
        }
    

    }

    FileObserver 简介
    这部分参考了博客(博客路径:https://www.robotshell.com/2017/08/30/Android-FileObserver%20%E7%B1%BB%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/)。

    android的FileObserver 是抽象类,是基于linux的inotify的特性来实现的,主要用来监控文件系统,根据文件的特定事件发出事件是。
    android 官方提供的参考路径:https://developer.android.com/reference/android/os/FileObserver

    处理ANR异常
    以上代码介绍到anr的处理函数handleAnr(String filepath)。以下为源码

    private void handleAnr(String filepath) {
    ...
    // 读取anr文件 /data/anr/trace*.txt。返回文件内容
    String trace = getTrace(filepath, anrTime.getTime());
    //删除其他的anr异常日志文件
    if (!FileManager.getInstance().maintainAnr()) {
    return;
    }
    //获取 tombstone 的文件头
    String emergency = null;
    try {
    emergency = getEmergency(anrTime, trace);
    } catch (Exception e) {
    XCrash.getLogger().e(Util.TAG, "AnrHandler getEmergency failed", e);
    }
    // 创建anr异常日志保存文件
    File logFile = null;
    try {
    String logPath = String.format(Locale.US, "%s/%s_%020d_%s__%s%s", logDir, Util.logPrefix, anrTime.getTime() * 1000, appVersion, processName, Util.anrLogSuffix);
    logFile = FileManager.getInstance().createLogFile(logPath);
    } catch (Exception e) {
    XCrash.getLogger().e(Util.TAG, "AnrHandler createLogFile failed", e);
    }
    if (logFile != null){
    // 根据配置将日志文件头,traces,logcat日志保存在文件中。
    }
    }

    anrCheckProcessState
    源码中有anrCheckProcessState属性。如果属性设置为true则会执行以下代码。

    // anr超时时间是15S
    private final long anrTimeoutMs = 15 * 1000;
    static boolean checkProcessAnrState(Context ctx, long timeoutMs) {
    ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
    if (am == null) return false;
    // 获取当前进程的id
    int pid = android.os.Process.myPid();
    long poll = timeoutMs / 500;
    for (int i = 0; i < poll; i++) {
    //获取系统中所有进程的错误信息
    List<ActivityManager.ProcessErrorStateInfo> processErrorList = am.getProcessesInErrorState();
    if (processErrorList != null) {
    for (ActivityManager.ProcessErrorStateInfo errorStateInfo : processErrorList) {
    // 进程ID和当前应用ID相同,且错误状态是ANR异常
    if (errorStateInfo.pid == pid && errorStateInfo.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) {
    return true;
    }
    }
    }

            try {
                Thread.sleep(500);
            } catch (Exception ignored) {
            }
        }
    
        return false;
    }
    

    通过以上代码,可以确定该函数的主要功能有:

    过滤掉其他应用的异常。
    过滤掉本应用非ANR异常。
    通过这个函数可以保证anrHandler处理的是当前应用的ANR异常。

    原文链接:https://blog.csdn.net/cxmfzu/article/details/103508028

    相关文章

      网友评论

          本文标题:xCrash 捕获ANR异常

          本文链接:https://www.haomeiwen.com/subject/kzhholtx.html