和你一起终身学习,这里是程序员Android
经典好文推荐,通过阅读本文,您将收获以下知识点:
一、Crash 概述
二、Crash处理流程
三、handleApplicationCrash处理分析
四、handleApplicationCrashInner 处理分析
五、APP Error info分析
六、makeAppCrashingLocked处理分析
七、startAppProblemLocked处理分析
八、stopFreezingAllLocked处理分析九、 AppErrors.handleAppCrashLocked()
十、UIHandler
十一、 killProcess
十二、小结
Android 9.0 Crash 机制调用链
/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java (含内部类AMP)
/frameworks/base/core/java/android/app/ApplicationErrorReport.java
/frameworks/base/services/core/java/com/android/server/
- am/ActivityManagerService.java
- am/ProcessRecord.java
- am/ActivityRecord.java
- am/ActivityStackSupervisor.java
- am/ActivityStack.java
- am/ActivityRecord.java
- am/BroadcastQueue.java
- wm/WindowManagerService.java
/libcore/libart/src/main/java/java/lang/Thread.java
一、Crash 概述
App Crash (全称Application Crash
), 对于Crash
可分为Native Crash和 Framework Crash(包含app crash在内),对于Crash
相信很多app开发者都会遇到,那么上层什么时候会出现Crash呢,系统又是如何处理Crash
的呢。例如,在app大家经常使用try...catch
语句,那么如果没有有效catch exception
,就是导致应用Crash,发生没有catch exception
,系统便会来进行捕获,并进入Crash
流程。如果你是从事Android系统开发或者架构相关工作,或者遇到需要解系统性的疑难杂症,那么很有必要了解系统Crash
处理流程,知其然还需知其所以然;如果你仅仅是App初级开发,可能本文并非很适合阅读,整个系统流程错综复杂。
在Android系统启动系列文章,已讲述过上层应用都是由Zygote fork孵化而来,分为system_server系统进程和各种应用进程,在这些进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时,最终都交给异常处理器。
对于
system_server
进程:system_server
启动过程中由RuntimeInit.java
的commonInit
方法设置UncaughtHandler
,用于处理未捕获异常;对于普通应用进程:进程创建过程中,同样会调用
RuntimeInit.java
的commonInit
方法设置UncaughtHandler
。
1.1 crash调用链
crash流程的方法调用关系如下:
AMP.handleApplicationCrash
AMS.handleApplicationCrash
AMS.findAppProcess
AMS.handleApplicationCrashInner
AMS.addErrorToDropBox
AMS.crashApplication
AMS.makeAppCrashingLocked
AMS.startAppProblemLocked
ProcessRecord.stopFreezingAllLocked
ActivityRecord.stopFreezingScreenLocked
WMS.stopFreezingScreenLocked
WMS.stopFreezingDisplayLocked
AMS.handleAppCrashLocked
mUiHandler.sendMessage(SHOW_ERROR_MSG)
Process.killProcess(Process.myPid());
System.exit(10);
二、Crash处理流程
RuntimeInit.java
类的 main
方法会调用commonInit()
方法。
public static final void main(String[] argv) {
enableDdms();
if (argv.length == 2 && argv[1].equals("application")) {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
redirectLogStreams();
} else {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
}
// AP Crash 处理流程初始化
commonInit();
// Native Crash 处理流程初始化
nativeFinishInit();
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}
那么接下来以commonInit()
方法为起点来展开说明。
1. RuntimeInit.commonInit
RuntimeInit.java
protected static final void commonInit() {
/*
* set handlers; these apply to all threads in the VM. Apps can replace
* the default handler, but not the pre handler.
*/
LoggingHandler loggingHandler = new LoggingHandler();
// app不能 替换 setUncaughtExceptionPreHandler
Thread.setUncaughtExceptionPreHandler(loggingHandler);
// 将异常处理器handler对象赋给Thread成员变量,
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
... ...
}
接下来我们看看LoggingHandler
的实现。LoggingHandler实现 Thread.UncaughtExceptionHandler 方法。
private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
public volatile boolean mTriggered = false;
@Override
public void uncaughtException(Thread t, Throwable e) {
mTriggered = true;
//保证crash处理过程不会重入
if (mCrashing) return;
//mApplicationObject等于null,一定不是普通的app进程.
//但是除了system进程, 也有可能是shell进程,
//即通过app_process + 命令参数 的方式创建的进程.
if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
//系统 进程Crash打印的Log 信息
/**
发生 系统Crash 时候可以搜索 关键字 FATAL EXCEPTION IN SYSTEM PROCESS
**/
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
/**
发生 APP Crash 时候可以搜索 关键字 FATAL EXCEPTION
**/
StringBuilder message = new StringBuilder();
message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
final String processName = ActivityThread.currentProcessName();
if (processName != null) {
message.append("Process: ").append(processName).append(", ");
}
message.append("PID: ").append(Process.myPid());
Clog_e(TAG, message.toString(), e);
}
}
}
1.当System进程Crash的信息:
开头 FATAL EXCEPTION IN SYSTEM PROCESS [线程名],接着输出Crash
时的调用栈信息;
2.当app进程Crash时的信息:
开头 FATAL EXCEPTION: [线程名],紧接着 Process: [进程名], PID: [进程id]
;最后输出发生Crash
时的调用栈信息。
看到这里,你就会发现要从log
中搜索Crash
信息,只需要搜索关键词 FATAL EXCEPTION,即可查看出是那种异常;如果需要进一步筛选只搜索系统crash信息,则可以搜索的关键词可以有多样,比如 FATAL EXCEPTION IN SYSTEM PROCESS。
当输出完Crash
信息到logcat
里面,这只是Crash
流程的刚开始阶段,接下来弹出Crash
对话框,ActivityManagerNative.getDefault()
返回的是ActivityManagerProxy(简称AMP)
,AMP
经过binder
调用最终交给ActivityManagerService(简称AMS)
中相应的方法去处理,然后调用的是AMS.handleApplicationCrash()
。
分析完LoggingHandler
后,我们继续看setDefaultUncaughtExceptionHandler()
,它只是将异常处理器handler
对象赋给Thread
成员变量,即Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
。接下来看看KillApplicationHandler
对象实例化过程。
2. KillApplicationHandler
RuntimeInit.java
KillApplicationHandler
实现 Thread.UncaughtExceptionHandler
方法,主要处理由于未捕获的异常Crash
导致APP 崩溃,运行在Main Thread
的Framework
代码会捕获这些异常。
KillApplicationHandler
方法需要传递一个LoggingHandler
的参数,
既 LoggingHandler loggingHandler = new LoggingHandler();
,LoggingHandler
在上文已经分析过,接下来我们看看KillApplicationHandler
方法.
KillApplicationHandler方法如下:
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
private final LoggingHandler mLoggingHandler;
public KillApplicationHandler(LoggingHandler loggingHandler) {
// 构造方法,初始化 loggingHandler
this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
try {
ensureLogging(t, e);
// 保证crash处理过程不会重入
if (mCrashing) return;
mCrashing = true;
... ...
//启动crash对话框,等待处理完成 【见小节2.1和3】
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
} catch (Throwable t2) {
... ...
} finally {
//确保当前进程彻底杀掉【见小节11】
Process.killProcess(Process.myPid());
System.exit(10);
}
}
... ...
}
接下来我们看看启动Crash
弹窗的处理。new ApplicationErrorReport.ParcelableCrashInfo(e)
方法。
2.1 ApplicationErrorReport.ParcelableCrashInfo
ApplicationErrorReport 主要用来描述 APP Error
信息。
APP ERROR 信息分类如下:
TYPE_CRASH:APP Crash 信息
TYPE_ANR:APP ANR 信息
TYPE_BATTERY:Battery 使用信息
TYPE_RUNNING_SERVICE:正在运行的Service 相关信息
// 主要处理 APP Error 信息
public class ApplicationErrorReport implements Parcelable {
... ...
public static class ParcelableCrashInfo extends CrashInfo implements Parcelable {
//创建 CrashInfo 实例,初始化异常信息
public ParcelableCrashInfo(Throwable tr) {
super(tr);
}
... ...
public static final Parcelable.Creator<ParcelableCrashInfo> CREATOR =
new Parcelable.Creator<ParcelableCrashInfo>() {
@Override
public ParcelableCrashInfo createFromParcel(Parcel in) {
return new ParcelableCrashInfo(in);
}
@Override
public ParcelableCrashInfo[] newArray(int size) {
return new ParcelableCrashInfo[size];
}
};
}
... ...
}
ParcelableCrashInfo
继承 CrashInfo
,接下来我们看看 CrashInfo
的实现。
CrashInfo
**CrashInfo ** 主要是将Crash
信息文件名,类名,方法名,对应行号以及异常信息都封装到CrashInfo
对象。
// 描述 Crash 信息
public static class CrashInfo {
... ...
public CrashInfo() {
}
//CrashInfo 初始化实例
public CrashInfo(Throwable tr) {
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 256);
//输出栈trace
tr.printStackTrace(pw);
pw.flush();
stackTrace = sanitizeString(sw.toString());
exceptionMessage = tr.getMessage();
// 显示异常的根本原因
Throwable rootTr = tr;
while (tr.getCause() != null) {
tr = tr.getCause();
if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) {
rootTr = tr;
}
String msg = tr.getMessage();
if (msg != null && msg.length() > 0) {
exceptionMessage = msg;
}
}
// Crash 异常类名称
exceptionClassName = rootTr.getClass().getName();
if (rootTr.getStackTrace().length > 0) {
StackTraceElement trace = rootTr.getStackTrace()[0];
// 获取 trace 文件名、类名、方法名、Crash 行号
throwFileName = trace.getFileName();
throwClassName = trace.getClassName();
throwMethodName = trace.getMethodName();
throwLineNumber = trace.getLineNumber();
} else {
throwFileName = "unknown";
... ...
}
exceptionMessage = sanitizeString(exceptionMessage);
}
三、handleApplicationCrash处理分析
handleApplicationCrash
会通过 JNI
接口调用AMS
中的方法。
//发送 Crash 弹窗handler,直到Dialog dismiss
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
ActivityManagerService.java
handleApplicationCrash
通过JNI
回调用 AMS
中的handleApplicationCrash
方法,进而调用AMS
中的内部方法handleApplicationCrashInner
。
handleApplicationCrash
1.当远程
IBinder
对象为空Null
时,则进程名为system_server
;2.当远程
IBinder