该系列文章总纲链接:专题总纲目录 Android Framework 总纲
本章关键点总结 & 说明:
说明:本章节主要解读APP Crash处理。关注思维导图中左上侧部分即可。
本章节主要是对Android的APP Crash处理有一个基本的了解。从进程启动到UncaughtHandler处理方法的注册到UncaughtHandler方法异常处理、AMS的binderDied讣告流程分析。以便于我们更好地理解APP 异常处理的闭环流程。
1 从进程启动到UncaughtHandler处理方法的注册
我们需要对开机启动一个进程有一个了解,相关参考文章如下:
android 开机启动流程分析(11)Zygote启动分析
基于以上文章的内容解读,在Zygote分裂时,最终会执行到runOnce方法,关键代码如下:
//ZygoteConnection
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
// ...
//fork进程操作
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
//...
try {
if (pid == 0) {
// 在子进程中执行
IoUtils.closeQuietly(serverPipeFd); // 关闭服务器端文件描述符
serverPipeFd = null; // 清除文件描述符引用
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); // 处理子进程
// 这里不应该到达,因为子进程应该要么抛出ZygoteInit.MethodAndArgsCaller异常,要么执行exec()。
return true;
} else {
// 在父进程中执行...pid小于0表示失败
IoUtils.closeQuietly(childPipeFd); // 关闭客户端文件描述符
childPipeFd = null; // 清除文件描述符引用
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); // 处理父进程
}
} finally {
IoUtils.closeQuietly(childPipeFd); // 最终关闭客户端文件描述符
IoUtils.closeQuietly(serverPipeFd); // 最终关闭服务器端文件描述符
}
}
在fork进程后,fokr出来的子进程会调用handleChildProc方法,我们主要关注该方法,代码实现如下:
//ZygoteConnection
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
// 关闭socket连接
closeSocket();
// 关闭Zygote服务器的socket
ZygoteInit.closeServerSocket();
// 如果提供了文件描述符数组
if (descriptors != null) {
try {
// 重定向标准输入、输出和错误流
ZygoteInit.reopenStdio(descriptors[0], descriptors[1], descriptors[2]);
// 关闭文件描述符数组中的所有文件描述符
for (FileDescriptor fd : descriptors) {
IoUtils.closeQuietly(fd);
}
// 设置新的标准错误流
newStderr = System.err;
} catch (IOException ex) {
// 记录重定向标准IO时的错误
Log.e(TAG, "Error reopening stdio", ex);
}
}
// 如果提供了nice名称(进程名),设置进程名
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
// 如果需要运行时初始化
if (parsedArgs.runtimeInit) {
// 如果提供了invokeWith(启动命令),使用WrapperInit启动应用程序
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {
// 否则,使用RuntimeInit启动应用程序
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
// 如果不需要运行时初始化,直接启动指定的类和主方法
String className;
try {
// 获取类名
className = parsedArgs.remainingArgs[0];
} catch (ArrayIndexOutOfBoundsException ex) {
// 如果类名参数缺失,记录错误并退出
logAndPrintError(newStderr,
"Missing required class name argument", null);
return;
}
// 准备主方法的参数
String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
System.arraycopy(parsedArgs.remainingArgs, 1,
mainArgs, 0, mainArgs.length);
// 如果提供了invokeWith(启动命令),使用WrapperInit启动独立应用程序
if (parsedArgs.invokeWith != null) {
WrapperInit.execStandalone(parsedArgs.invokeWith,
parsedArgs.classpath, className, mainArgs);
} else {
// 否则,使用ClassLoader加载类并执行主方法
ClassLoader cloader;
if (parsedArgs.classpath != null) {
cloader = new PathClassLoader(parsedArgs.classpath,
ClassLoader.getSystemClassLoader());
} else {
cloader = ClassLoader.getSystemClassLoader();
}
try {
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
} catch (RuntimeException ex) {
// 如果启动过程中出现异常,记录错误
logAndPrintError(newStderr, "Error starting.", ex);
}
}
}
}
这里如果是第一次启动APP进程,那么parsedArgs.runtimeInit的值为true,invokeWith为null,这时候会调用到RuntimeInit.zygoteInit方法,该方法实现如下:
//RuntimeInit
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
// 重定向日志流,使得应用程序的日志能够被正确地输出
redirectLogStreams();
// 执行公共初始化操作,这部分代码涉及到设置进程的基本信息,如进程名称等
commonInit();
// 调用本地方法进行Zygote初始化,这可能涉及到JNI调用,设置本地环境等
nativeZygoteInit();
// 执行应用程序特定的初始化操作,这部分代码会根据传入的参数和类加载器加载应用程序的类,并启动应用程序的主线程
applicationInit(targetSdkVersion, argv, classLoader);
}
这里因为我们主要关注未处理的异常处理,这里的commonInit关键代码实现如下:
//RuntimeInit
private static final void commonInit() {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
//...
initialized = true;
}
这里当APP抛出未处理的异常时,都是由UncaughtHandler来处理的。到此才是APP异常处理的逻辑。
注意:如果应用程序在Android中自行设定了UncaughtExceptionHandler,这将优先于系统默认的异常处理机制。因此,当应用程序发生未捕获异常时,系统框架的崩溃处理流程将不会被触发,而是执行自定义的异常处理逻辑。通过这种方式,可以实现错误日志的自动上报功能,并且在捕获异常时防止应用程序崩溃,避免显示崩溃对话框。简而言之,自定义的UncaughtExceptionHandler允许APP开发者接管异常处理,进行日志记录和稳定性维护,而不采用系统的默认崩溃响应。
2 UncaughtHandler方法异常处理解读
UncaughtHandler的代码实现如下:
//RuntimeInit
private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
// 处理未捕获异常的方法
public void uncaughtException(Thread t, Throwable e) {
try {
// 避免重复进入——如果崩溃报告本身崩溃,避免无限循环。
if (mCrashing) return;
mCrashing = true;
// 如果mApplicationObject为空,表示系统进程发生了致命异常
if (mApplicationObject == null) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);