xCrash 能为安卓 app 提供捕获 java 崩溃,native 崩溃和 ANR 的能力。不需要 root 权限或任何系统权限。
https://github.com/iqiyi/xCrash
Java 崩溃的捕获很简单,通过 UncaughtExceptionHandler
接口实现:
class JavaCrashHandler implements UncaughtExceptionHandler {
void initialize(int pid, String processName, String appId, String appVersion, String logDir, boolean rethrow,
int logcatSystemLines, int logcatEventsLines, int logcatMainLines,
boolean dumpFds, boolean dumpNetworkInfo, boolean dumpAllThreads, int dumpAllThreadsCountMax, String[] dumpAllThreadsWhiteList,
ICrashCallback callback) {
...
// 获取原来的 handler
this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置 JavaCrashHandler 对象
try {
Thread.setDefaultUncaughtExceptionHandler(this);
} catch (Exception e) {
XCrash.getLogger().e(Util.TAG, "JavaCrashHandler setDefaultUncaughtExceptionHandler failed", e);
}
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
if (defaultHandler != null) {
Thread.setDefaultUncaughtExceptionHandler(defaultHandler);
}
try {
handleException(thread, throwable);
} catch (Exception e) {
XCrash.getLogger().e(Util.TAG, "JavaCrashHandler handleException failed", e);
}
if (this.rethrow) {
if (defaultHandler != null) {
defaultHandler.uncaughtException(thread, throwable);
}
} else {
ActivityMonitor.getInstance().finishAllActivities();
Process.killProcess(this.pid);
System.exit(10);
}
}
...
}
handleException(thread, throwable)
方法收集各种信息rethrow
为 true,调用原来的 UncaughtExceptionHandler
的 uncaughtException
,否则关闭所有的 Activity,杀掉本进程并退出handleException(thread, throwable) 方法
private void handleException(Thread thread, Throwable throwable) {
Date crashTime = new Date();
//notify the java crash
NativeHandler.getInstance().notifyJavaCrashed();
AnrHandler.getInstance().notifyJavaCrashed();
//create log file
File logFile = null;
try {
String logPath = String.format(Locale.US, "%s/%s_%020d_%s__%s%s", logDir, Util.logPrefix, startTime.getTime() * 1000, appVersion, processName, Util.javaLogSuffix);
logFile = FileManager.getInstance().createLogFile(logPath);
} catch (Exception e) {
XCrash.getLogger().e(Util.TAG, "JavaCrashHandler createLogFile failed", e);
}
//get emergency
String emergency = null;
try {
emergency = getEmergency(crashTime, thread, throwable);
} catch (Exception e) {
XCrash.getLogger().e(Util.TAG, "JavaCrashHandler getEmergency failed", e);
}
//write info to log file
if (logFile != null) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(logFile, "rws");
//write emergency info
if (emergency != null) {
raf.write(emergency.getBytes("UTF-8"));
}
//If we wrote the emergency info successfully, we don't need to return it from callback again.
emergency = null;
//write logcat
if (logcatMainLines > 0 || logcatSystemLines > 0 || logcatEventsLines > 0) {
raf.write(Util.getLogcat(logcatMainLines, logcatSystemLines, logcatEventsLines).getBytes("UTF-8"));
}
//write fds
if (dumpFds) {
raf.write(Util.getFds().getBytes("UTF-8"));
}
//write network info
if (dumpNetworkInfo) {
raf.write(Util.getNetworkInfo().getBytes("UTF-8"));
}
//write memory info
raf.write(Util.getMemoryInfo().getBytes("UTF-8"));
//write background / foreground
raf.write(("foreground:\\\\n" + (ActivityMonitor.getInstance().isApplicationForeground() ? "yes" : "no") + "\\\\n\\\\n").getBytes("UTF-8"));
//write other threads info
if (dumpAllThreads) {
raf.write(getOtherThreadsInfo(thread).getBytes("UTF-8"));
}
} catch (Exception e) {
XCrash.getLogger().e(Util.TAG, "JavaCrashHandler write log file failed", e);
} finally {
if (raf != null) {
try {
raf.close();
} catch (Exception ignored) {
}
}
}
}
//callback
if (callback != null) {
try {
callback.onCrash(logFile == null ? null : logFile.getAbsolutePath(), emergency);
} catch (Exception ignored) {
}
}
}
getEmergency(crashTime, thread, throwable)
获取堆栈信息getEmergency(crashTime, thread, throwable)