概述:
Android是基于java,虚拟机采用的是ART.而在应用进程启动的时候,在通过runtimeinit中的commoninit方法,设置了进程的defaultuncaughtexceptionhandler。那我们就从这里入手。让我们带着问题来一起看一下。本文基于Android Q版本。
1.什么时候设置的uncaughtexceptionhandler?
2.什么时候会触发uncaughtexceptionhandler?
第一个问题:
@UnsupportedAppUsage
protected static final void commonInit() {
// 在commoninit的时候创建了killapplicationhandler , 其中的logginghandler只是为了打印log
RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
}
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
private final LoggingHandler mLoggingHandler;
@Override
public void uncaughtException(Thread t, Throwable e) {
try {
// 打印log
ensureLogging(t, e);
// bindercall到systemserver
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
} catch (Throwable t2) {
...
} finally {
// 确保彻底杀死了本进程
Process.killProcess(Process.myPid());
System.exit(10);
}
}
上面这些其实都是发生在应用进程中,当应用发生崩溃的时候,就会通过binder调用,来将crashinfo传递给systemserver,而parcelablecrashinfo也是通过throwable e来进行封装创建的。
下面就通过binder调用,来看看systemserver所进行的操作。
ams->handleApplicationCrash
public void handleApplicationCrash(IBinder app,
ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
ProcessRecord r = findAppProcess(app, "Crash");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
handleApplicationCrashInner("crash", r, processName, crashInfo);
}
首先通过ibinder找到对应的processrecord,然后调用handleapplicationcrashinner
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
// 省略打印log的部分
final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
: r.getWindowProcessController().computeRelaunchReason();
final String relaunchReasonString = relaunchReasonToString(relaunchReason);
if (crashInfo.crashTag == null) {
crashInfo.crashTag = relaunchReasonString;
} else {
crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
}
// 将错误信息打印到dropbox
addErrorToDropBox(
eventType, r, processName, null, null, null, null, null, null, crashInfo);
mAppErrors.crashApplication(r, crashInfo);
}
// AppErrors.java
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
int callingPid, int callingUid) {
AppErrorResult result = new AppErrorResult();
int taskId;
synchronized (mService) {
// 在monkey的情况
if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
timeMillis, callingPid, callingUid)) {
return;
}
AppErrorDialog.Data data = new AppErrorDialog.Data();
data.result = result;
data.proc = r;
if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
return;
}
final Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
taskId = data.taskId;
msg.obj = data;
mService.mUiHandler.sendMessage(msg);
}
// 弹出系统错误对话框,等待用户点击
int res = result.get();
...
}
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
app.setCrashing(true);
app.crashingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
app.startAppProblemLocked();
app.getWindowProcessController().stopFreezingActivities();
return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
data);
}
其实后面就是对应的ams和wms相关的系统层面的操作了。如切换当前的topapp,修改对应的app的windows状态,以及input的focus问题。这里就不赘述了。
那么其实现在就可以回答第一个问题了: 什么时候设置的uncaughtexceptionhandler?
答案就是:在应用启动的时候,系统会在runtimeinit的commoninit设置对应的defaultuncaughtexceptionhandler。
第二个问题:
我们都知道,Android进程其实是运行在ART虚拟机当中,当运行的代码发生了错误的时候,虚拟机会调用Thread::HandleUncaughtExceptions
// thread.cc
void Thread::HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa) {
// Call the Thread instance's dispatchUncaughtException(Throwable)
tlsPtr_.jni_env->CallVoidMethod(peer.get(),
WellKnownClasses::java_lang_Thread_dispatchUncaughtException,
exception.get());
}
通过jni的方式调用到了java层
// libcore/ojluni/src/main/java/java/lang/Thread.java
public final void dispatchUncaughtException(Throwable e) {
// BEGIN Android-added: uncaughtExceptionPreHandler for use by platform.
Thread.UncaughtExceptionHandler initialUeh =
Thread.getUncaughtExceptionPreHandler();
if (initialUeh != null) {
try {
initialUeh.uncaughtException(this, e);
} catch (RuntimeException | Error ignored) {
// Throwables thrown by the initial handler are ignored
}
}
// END Android-added: uncaughtExceptionPreHandler for use by platform.
getUncaughtExceptionHandler().uncaughtException(this, e);
}
上面的流程中就可以和第一个问题串联在一起了。在发生异常的时候,虚拟机得到对应的uncaughtexceptionhandler,调用其对应的uncaughtexception方法。
但是需要注意的是,这里有两个exceptionhandler的实现。分别是PreHandler和Handler。
需要注意的是:
1.setDefaultUncaughtExceptionHandler是公开API。应用可通过调用,自定义。
2.setUncaughtExceptionPreHandler是hidden API。应用无法调用,不能替换
具体的可以查看这个链接,从注释中可以看到,这么做的目的,是为了不管应用是否替换了handler,系统都能够打印出对应的崩溃日志。
那么我们就可以回答第二个问题了:
在ART虚拟机发生错误的时候,会调用注册在thread中的handler,通过handler的uncaughtexception来告知当前线程发生了崩溃。
总结:
javacrash,因为都是发生在ART虚拟机中,而ART虚拟机在启动的时候,注册了2个handler来处理错误,一个用来打印log,而另外一个用来杀死进程。APP可以通过设置第二个handler来进行自定义的java crash的处理。