xCrash 捕获ANR异常

转自: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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容