当前位置: 首页 > 工具软件 > xCrash > 使用案例 >

xCrash使用篇

满言
2023-12-01

简介

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

使用

  1. 添加依赖
dependencies {
    implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.6'
}

2.需要捕获native异常,或者android的api版本大于21。需要添加以下配置,用于捕获异常。

android {
	defaultConfig {
        ndk {
        	// 根据需要添加必要的ABI
            abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }
}
  1. 初始化xCrash
public class MyCustomApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        //  默认初始化
        XCrash.init(this);
    }
}

Tombstone 文件默认将被写入到 Context#getFilesDir() + “/tombstones” 目录。(通常在: /data/data/PACKAGE_NAME/files/tombstones)。
xCrash提供两个接口用于APP应用初始化xCrash。
初始化接口1:

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

初始化接口2:

public static synchronized int init(Context ctx, InitParameters params){
	...
}

初始化接口1和接口2都需要传入context用于xCrash获取APP应用进程的信息,不同的是接口1,不传入自定义配置,采用的是xCrash的默认配置,接口2,可以根据APP应用的需要自定义配置。

配置

xCrash提供可选的配置参数,通过可选的配置参数保存APP应用进程发生异常。xCrash的初始化配置参见XCrash.java中的内部类InitParameters。
xCrash配置参数分为四个部分:1.通用输出配置;2.java异常输出配置;3.native异常输出配置;4.ANR异常输出配置。

通用输出配置

配置源码参见源文件XCrash.java中内部类InitParameters。

public static class InitParameters{
		...
		// APP应用版本信息
        String     appVersion             = null;
        // 用于存放异常日志的文件夹
        String     logDir                 = null;
        // 初始化xCrash后延迟xx毫秒,进行日志文件维护任务(为清理多余的过期日志文件)
        int        logFileMaintainDelayMs = 5000;
        // xCrash库的日志输出接口
        ILogger    logger                 = null;
        // xCrash库需要加载的so文件路径
        ILibLoader libLoader              = null;
        ...
}

Java异常输出配置

配置源码参见源文件XCrash.java中内部类InitParameters。

public static class InitParameters{
		...
		// 是否使能Java异常处理器 
	    boolean        enableJavaCrashHandler      = true;
	    // 是否抛出Java异常
        boolean        javaRethrow                 = true;
        // java异常文件数量
        int            javaLogCountMax             = 10;
        // 执行命令 logcat -b system 输出的日志行数 
        int            javaLogcatSystemLines       = 50;
        // 执行命令 logcat -b event 输出的日志行数
        int            javaLogcatEventsLines       = 50;
        // logcat -b main 输出的日志行数
        int            javaLogcatMainLines         = 200;
        // 是否输出app应用进程的文件描述符
        boolean        javaDumpFds                 = true;
        // 是否输出所有线程的日志信息
        boolean        javaDumpAllThreads          = true;
        // 输出日志最多的线程数
        int            javaDumpAllThreadsCountMax  = 0;
        // 输出线程日志的线程白名单
        String[]       javaDumpAllThreadsWhiteList = null;
        // 发生java异常crash的回调
        ICrashCallback javaCallback                = null;
        ...
}

native异常输出配置

配置源码参见源文件XCrash.java中内部类InitParameters。

public static class InitParameters{
	...
		// 是否输出native异常标志
	    boolean        enableNativeCrashHandler      = true;
	    // 是否抛出异常
        boolean        nativeRethrow                 = true;
        // native异常文件的最大数量
        int            nativeLogCountMax             = 10;
        // 执行命令 logcat -b system 输出的日志行数 
        int            nativeLogcatSystemLines       = 50;
        // 执行命令 logcat -b evnet输出的日志行数 
        int            nativeLogcatEventsLines       = 50;
         // logcat -b main 输出的日志行数
        int            nativeLogcatMainLines         = 200;
        // 是否输出产生native异常的so库文件的hash值
        boolean        nativeDumpElfHash             = true;
        // 是否输出产生native异常so文件的内存映射
        boolean        nativeDumpMap                 = true;
        // 是否输出文件描述符
        boolean        nativeDumpFds                 = true;
        // 是否输出所有线程的日志信息,默认为true,如果为false只输出crahs线程的信息
        boolean        nativeDumpAllThreads          = true;
        // 输出日志最多的线程数
        int            nativeDumpAllThreadsCountMax  = 0;
        // 输出线程日志的线程白名单
        String[]       nativeDumpAllThreadsWhiteList = null;
        // 发生java异常crash的回调
        ICrashCallback nativeCallback                = null;
	...
}

ANR异常输出配置

配置源码参见源文件XCrash.java中内部类InitParameters

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如何配置异常日志输出,可以参见源码中的初始化接口。

public final class XCrash {
	// xcrash 初始化标志
    private static boolean initialized = false;
    // app应用的appId
    private static String appId = null;
    // app应用版本的版本信息
    private static String appVersion = null;
    // 日志输出的文件夹路径
    private static String logDir = null;
    // xcrash默认的日志输出接口
    private static ILogger logger = new DefaultLogger();
    public static int init(Context ctx) {
    	// 调用内部的初始化函数
        return init(ctx, null);
    }
    // 初始化配置函数
    public static synchronized int init(Context ctx, InitParameters params) {
    	//  判断是否已初始化过,如果初始化过,不允许初始化两次
    	if (XCrash.initialized) {
            return Errno.OK;
        }
        ...
        // 默认配置
        if (params == null) {
        	// 默认配置的可选配置项参见上一小结的内容
            params = new InitParameters();
        }
        // 通用配置,xcrash的日志输出接口。
        // 如果通过配置参数进行配置,则xcrash库的日志输出库为参数配置的接口为准。
        // 如果不配置,则为DefaultLogger()。DefaultLogger为调用android.util.Log输出日志
        if (params.logger != null) {
            XCrash.logger = params.logger;
        }
        // 保存 app 应用的appId
        String packageName = ctx.getPackageName();
        XCrash.appId = packageName;
        // 不存在,保存为unknown
        if (TextUtils.isEmpty(XCrash.appId)) {
            XCrash.appId = "unknown";
        }
        if (TextUtils.isEmpty(params.appVersion)) {
        	// 获取app的版本,使用Android Studio,则为app.build文件中的versionName
            params.appVersion = Util.getAppVersion(ctx);
        }
        // 设置异常日志的输出文件夹路径。
        // 默认为 /data/data/PACKAGE_NAME/files/tombstones
        if (TextUtils.isEmpty(params.logDir)) {
            params.logDir = ctx.getFilesDir() + "/tombstones";
        }
        XCrash.logDir = params.logDir;
        // 获取应用的进程名和进程ID
        int pid = android.os.Process.myPid();
        String processName = null;
        ...
        //  日志文件的管理器  异常日志文件数量,超过数量,删除最早的。
        // 其中异常日志的占位文件,在native异常或者android 5.0以后anr异常会使用到
        // 在捕获到native异常的时候,如果存在占位文件,则使用占位文件。不存在才会创建新的文件
        FileManager.getInstance().initialize(
            params.logDir,// 异常日志文件路径
            params.javaLogCountMax,// java异常日志文件数量
            params.nativeLogCountMax,
            params.anrLogCountMax,
            params.placeholderCountMax, // 异常日志的占位文件,可配置
            params.placeholderSizeKb,// 异常日志占位文件的大小
            params.logFileMaintainDelayMs // xCrash初始化后,延迟xx毫秒,进行日志文件管理。
            );
			// 是否捕获java异常日志
            if (params.enableJavaCrashHandler) {
            JavaCrashHandler.getInstance().initialize(
                pid, 
                processName, 
                appId,
                params.appVersion,
                params.logDir,
                params.javaRethrow,
                params.javaLogcatSystemLines,
                params.javaLogcatEventsLines,
                params.javaLogcatMainLines,
                params.javaDumpFds,
                params.javaDumpAllThreads,
                params.javaDumpAllThreadsCountMax,
                params.javaDumpAllThreadsWhiteList,
                params.javaCallback);
        }
     //init ANR handler (API level < 21)
        if (params.enableAnrHandler && Build.VERSION.SDK_INT < 21) {
            AnrHandler.getInstance().initialize(
                ctx,
                pid,
                processName,
                appId,
                params.appVersion,
                params.logDir,
                params.anrCheckProcessState,
                params.anrLogcatSystemLines,
                params.anrLogcatEventsLines,
                params.anrLogcatMainLines,
                params.anrDumpFds,
                params.anrCallback);
        }
         //init native crash handler / ANR handler (API level >= 21)
        int r = Errno.OK;
        if (params.enableNativeCrashHandler || (params.enableAnrHandler && Build.VERSION.SDK_INT >= 21)) {
            r = NativeHandler.getInstance().initialize(
                ctx,
                params.libLoader,
                appId,
                params.appVersion,
                params.logDir,
                params.enableNativeCrashHandler,
                params.nativeRethrow,
                params.nativeLogcatSystemLines,
                params.nativeLogcatEventsLines,
                params.nativeLogcatMainLines,
                params.nativeDumpElfHash,
                params.nativeDumpMap,
                params.nativeDumpFds,
                params.nativeDumpAllThreads,
                params.nativeDumpAllThreadsCountMax,
                params.nativeDumpAllThreadsWhiteList,
                params.nativeCallback,
                params.enableAnrHandler && Build.VERSION.SDK_INT >= 21,
                params.anrRethrow,
                params.anrCheckProcessState,
                params.anrLogcatSystemLines,
                params.anrLogcatEventsLines,
                params.anrLogcatMainLines,
                params.anrDumpFds,
                params.anrCallback);
        }

       // 执行日志文件管理
        FileManager.getInstance().maintain();

        return r;
    }
}

xCrash初始化接口,主要对配置信息保存,以及执行日志文件管理。xCrahs配置的可以参见源码提供的xCrash_sample工程。

异常日志格式分析

通过xCrash_sample工程可以对android常见的异常进行测试。下面对日志文件结构进行简单的分析,以便应用发生异常是,通过日志来分析原因。以下日志文件,为采用默认配置输出的异常日志。
异常日志文件。日志文件的组成部分:tombstone_应用启动时间*1000_app版本_app进程名_java异常后缀。

	// java 异常文件后缀
	static final String javaLogSuffix = ".java.xcrash";
	// native异常文件后缀
    static final String nativeLogSuffix = ".native.xcrash";
    // anr异常文件后缀,api版本小于21
    static final String anrLogSuffix = ".anr.xcrash";
     // anr异常文件后缀,api版本大于21
    static final String traceLogSuffix = ".trace.xcrash";

举例如下:

tombstone_00001571190344276000_1.2.3-beta456-patch789__xcrash.sample.java.xcrash

因此我们可以通过文件名可以获取到部分有用的信息,比如说发生异常的app应用的版本信息,以及发生异常的时间(如果换位年月日时分秒毫秒可能更为明显),异常的类型。

Java异常输出日志

日志主要分为

  1. 头部信息(为应用的基本信息)
  2. java stacktrace
  3. logcat日志输出部分,包括main,system,event
  4. app应用进程打开的文件描述符
  5. 内存信息
  6. app应用进程信息
  7. 异常回调填充信息
//   头部信息
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Tombstone maker: 'xCrash 2.4.5'
Crash type: 'java'
Start time: '2019-10-16T09:45:44.276+0800'
Crash time: '2019-10-16T09:45:49.395+0800'
App ID: 'xcrash.sample'
App version: '1.2.3-beta456-patch789'
Rooted: 'No'
API level: '25'
OS version: '7.1.2'
ABI list: 'arm64-v8a,armeabi-v7a,armeabi'
Manufacturer: 'LANDI'
Brand: 'LANDI'
Model: 'AECR C10'
Build fingerprint: 'Android/msm8953_64/msm8953_64:7.1.2/N2G47H/builde01180925:user/test-keys'
pid: 22023, tid: 22023, name: main  >>> xcrash.sample <<<
//  java stacktrace
java stacktrace:
...
// logcat日志输出部分,包括main,system,event
logcat:
// app应用进程打开的文件描述符
open files:
...
// app应用的内存信息
memory info:
 System Summary (From: /proc/meminfo)
// 进程信息
 Process Status (From: /proc/PID/status)
 ...
 Process Limits (From: /proc/PID/limits)
 +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
// 异常回调填充信息(应用自定的信息,参见ICrashCallback )

Native异常输出日志

日志分为:

  1. 头部信息(为应用的基本信息)
  2. 异常信号部分。(哪个异常信号导致异常,信号参见Linux信号)
  3. backtrace
  4. so库的编译信息,build id
  5. 堆栈信息
  6. 内存信息
  7. 内存映射
  8. logcat日志输出部分,包括main,system,event
  9. app应用进程打开的文件描述符
  10. 内存信息
  11. app应用进程信息
  12. 异常回调填充信息
// 信息头部
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Tombstone maker: 'xCrash 2.4.5'
Crash type: 'native'
Start time: '2019-10-16T16:06:18.882+0800'
Crash time: '2019-10-16T16:06:24.668+0800'
App ID: 'xcrash.sample'
App version: '1.2.3-beta456-patch789'
Rooted: 'No'
API level: '25'
OS version: '7.1.2'
Kernel version: 'Linux version 3.18.31-perf #1 SMP PREEMPT Fri Jan 18 09:50:24 CST 2019 (aarch64)'
ABI list: 'arm64-v8a,armeabi-v7a,armeabi'
Manufacturer: 'LANDI'
Brand: 'LANDI'
Model: 'AECR C10'
Build fingerprint: 'Android/msm8953_64/msm8953_64:7.1.2/N2G47H/builde01180925:user/test-keys'
ABI: 'arm64'
pid: 23897, tid: 23897, name: xcrash.sample  >>> xcrash.sample <<<
// 异常信号部分
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
// 异常堆栈信息
backtrace:
// so库的编译信息,build id
build id:
// 堆栈信息
stack:
// 内存信息
memory near xxx:
// 内存映射信息
memory map:
// logcat日志输出部分,包括main,system,event
logcat:
// app应用进程打开的文件描述符
open files:
// 内存信息
memory info:
// 进程信息
Process Limits (From: /proc/PID/limits)
Process Details (From: /proc/PID/smaps)
Process Dalvik Details (From: /proc/PID/smaps)
Process Summary (From: /proc/PID/smaps)

anr异常输出日志

  1. 头部信息(为应用的基本信息)
  2. 异常信号部分。(哪个异常信号导致异常,信号参见Linux信号)
  3. backtrace
  4. 主线程信息
  5. 内存映射
  6. logcat日志输出部分,包括main,system,event
  7. app应用进程打开的文件描述符
  8. 内存信息
  9. app应用进程信息
  10. 异常回调填充信息
//  应用基本信息
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Tombstone maker: 'xCrash 2.4.6'
..
// 异常信号部分
Cmd line: xcrash.sample
Mode: ART DumpForSigQuit
//  主线程信息
DALVIK THREADS (18):
"main" prio=5 tid=1 Sleeping
// logcat日志输出部分,包括main,system,event
logcat:
// app应用进程打开的文件描述符
open files:
// 内存信息
memory info:
// 进程信息
Process Limits (From: /proc/PID/limits)
Process Details (From: /proc/PID/smaps)
Process Dalvik Details (From: /proc/PID/smaps)
Process Summary (From: /proc/PID/smaps)

小结

通过以上的介绍,可以了解xCrash库的基本使用,以及通过xCrash来捕获android应用的异常,通过对简单的异常日志文件可以进行分析,用来定位发生异常的具体位置。后续将对java异常,native异常,anr异常进行源码分析,借此加深对异常系统的理解。
请各位方家不吝赐教,谢谢!

 类似资料: