Jave Crash 处理流程
[基于 android P]
一、概述
App crash(全称Application crash), 对于Crash可分为native crash和java crash,对于crash相信很多app开发者都会遇到,那么上层什么时候会出现crash呢,系统又是如何处理crash的呢。例如,在app大家经常使用try…catch语句,那么如果没有有效catch exception,就是导致应用crash,系统便会来捕获,并进入crash流程。
我们知道上层应用进程都是由Zygote fork而来,分为system_server系统进程和各种应用进程,在这些进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时,最终都交给异常处理器,即RuntimeInit.java的commonInit方法设置UncaughtHandler来捕获异常。
crash处理流程的堆栈调用关系:
RuntimeInit.setDefaultUncaughtExceptionHandler
RuntimeInit.KillApplicationHandler.uncaughtException
AM.getService().handleApplicationCrash(binder通信)
AMS.handleApplicationCrash
AMS.findAppProcess
AMS.handleApplicationCrashInner
AMS.addErrorToDropBox
AppErr.crashApplication
AppErr.crashApplicationInner
AMS.makeAppCrashingLocked
AMS.startAppProblemLocked
ProcessRecord.stopFreezingAllLocked
ActivityRecord.stopFreezingScreenLocked
WMS.stopFreezingDisplayLocked
AMS.handleAppCrashLocked
mUiHandler.sendMessage(SHOW_ERROR_UI_MSG)
Process.killProcess(Process.myPid());
System.exit(10);
二、Crash处理流程
那么接下来以commonInit()方法为起点来梳理流程。
1. RuntimeInit.commonInit
public class RuntimeInit {
...
private static final void commonInit() {
//设置默认的未捕获异常处理器,UncaughtHandler实例化过程
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
...
}
}
setDefaultUncaughtExceptionHandler()只是将异常处理器handler对象赋给Thread成员变量,接下来看看KillApplicationHandler对象实例化过程。
2. UncaughtHandler
[–>RuntimeInit.java]
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler{
//覆写接口方法
public void uncaughtException(Thread t, Throwable e) {
try {
ensureLogging(t, e);//【2.1】
......
// 【3】
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
} catch (Throwable t2) {
......
} finally {
// 确保进程被杀死【11】
Process.killProcess(Process.myPid());
System.exit(10);
}
}
2.1 ensureLogging
[–> RuntimeInit.java]
private void ensureLogging(Thread t, Throwable e) {
if (!mLoggingHandler.mTriggered) {
try {
//【2.2】
mLoggingHandler.uncaughtException(t, e);
} catch (Throwable loggingThrowable) {
// Ignored.
}
}
}
2.2 uncaughtException
[-> RuntimeInit.LoggingHandler]
private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
public volatile boolean mTriggered = false;
@Override
public void uncaughtException(Thread t, Throwable e) {
mTriggered = true;
if (mCrashing) return;
//注意: mApplicationObject等于null,一定不是普通的app进程.
//除了system进程, 也有可能是shell进程, 即通过app_process + 命令参数 的方式创建的进程。
if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
// The "FATAL EXCEPTION" string is still used on Android even though
// apps can set a custom UncaughtExceptionHandler that renders uncaught
// exceptions non-fatal.
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);
}
}
}
当system进程crash的信息:
开头*** FATAL EXCEPTION IN SYSTEM PROCESS [线程名];
接着输出发生crash时的调用栈信息;
当app进程crash时的信息:
开头FATAL EXCEPTION: [线程名];
紧接着 Process: [进程名], PID: [进程id];
最后输出发生crash时的调用栈信息,
app crash 保存在crash_log中。
当输出完crash信息到logcat里面,这只是crash流程的刚开始阶段,接下来弹出crash对话框,经过binder调用最终交给ActivityManagerService(简称AMS)中相应的方法去处理,故接下来调用的是AMS.handleApplicationCrash()。
3. handleApplicationCrash
[–>ActivityManagerService.java]
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
//获取Processrecord对象 【3.1】
ProcessRecord r = findAppProcess(app, "Crash");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
//【4】
handleApplicationCrashInner("crash", r, processName, crashInfo);
}
关于进程名(processName):
- 当远程IBinder对象为空时,则进程名为system_server;
- 当远程IBinder对象不为空,且ProcessRecord为空时,则进程名为unknown;
- 当远程IBinder对象不为空,且ProcessRecord不为空时,则进程名为ProcessRecord对象中相应进程名。
3.1 findAppProcess
[–>ActivityManagerService.java]
private ProcessRecord findAppProcess(IBinder app, String reason) {
if (app == null) {
return null;
}
synchronized (this) {
final int NP = mProcessNames.getMap().size();
for (int ip=0; ip<NP; ip++) {
SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord p = apps.valueAt(ia);
//当找到目标进程则返回
if (p.thread != null && p.thread.asBinder() == app) {
return p;
}
}
}
//如果代码执行到这里,表明无法找到应用所在的进程
return null;
}
}
其中 mProcessNames = new ProcessMap();对于代码mProcessNames.getMap()返回的是mMap,而mMap= new ArrayMap<String, SparseArray>();
知识延伸:SparseArray和ArrayMap是Android专门针对内存优化而设计的取代Java API中的HashMap的数据结构。对于key是int类型则使用SparseArray,可避免自动装箱过程;对于key为其他类型则使用ArrayMap。HashMap的查找和插入时间复杂度为O(1)的代价是牺牲大量的内存来实现的,而SparseArray和ArrayMap性能略逊于HashMap,但更节省内存。
再回到mMap,这是以进程name为key,再以(uid为key,以ProcessRecord为Value的)结构体作为value。
有了进程记录对象ProcessRecord和进程名processName,则进入执行Crash处理方法。