Android Crash处理流程分析
Android的crash主要有3种,java层的force close,native层的crash和ANR。检查这三种crash的log方法也不相同:分别搜索“FATAL EXCEPTION”, “fault addr”和”ANR”。这三种crash的处理流程有不同,也有很多共性,但不管哪种crash,都先要导致了解下App的启动流程。
App的启动流程
App启动时,如果不和其他应用shareUserId,将会为自己创建一个进程空间,是通过调用ActivityManagerService的startProcessLocked()完成的,而Android的crash主要有3种,java层的force close,native层的crash和ANR。检查这三种crash的log方法也不相同:分别搜索“FATAL EXCEPTION”, “fault addr”和”ANR”。这三种crash的处理流程有不同,也有很多共性,但不管哪种crash,都先要导致了解下App的启动流程。
App的启动流程
App启动时,如果不和其他应用shareUserId,将会为自己创建一个进程空间,是通过调用ActivityManagerService的startProcessLocked()完成的,用下面的流程图表示:
第4步是不准确的,Zygote起来后,ZygoteInit就把runSelectLoop起来了,一直在等着有人通过socket连接它。第7步中,已经fork了App进程,从这后面的代码就是运行在App的进程中了,下面重点看下commonInit函数的处理流程。
Force close的处理流程
UncaughtHandler
App之所以出现了force close,就是有异常抛出,而App应用没有catch到。上节说到commonInit,这儿看下commonInit的代码:
private static final void commonInit() {
......
/* set default handler; this applies to all threads in the VM */
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
......
}
设置了一个默认的异常处理器。
private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
try {
// Don't re-enter -- avoid infinite loops if crash-reporting crashes.
if (mCrashing) return;
mCrashing = true;
if (mApplicationObject == null) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
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);
}
......
// Bring up crash dialog, wait for it to be dismissed
ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
} catch (Throwable t2) {
if (t2 instanceof DeadObjectException) {
// System process is dead; ignore
} else {
try {
Clog_e(TAG, "Error reporting crash", t2);
} catch (Throwable t3) {
// Even Clog_e() fails! Oh well.
}
}
} finally {
// Try everything to make sure this process goes away.
Process.killProcess(Process.myPid());
System.exit(10);
}
}
}
在这里面会打出异常栈中的信息,之所以force close搜索时使用“FATAL EXCEPTION”也是在这儿体现的。
ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e))从注释上看是要调用crash的对话框,过会看下它,在finally的处理中,会将发生异常的进程干掉。
handleApplicationCrash的处理流程
ActivityManagerNative.getDefault()返回的ActivityManagerProxy的实例,每一个应用进程中都有一个唯一的ActivityManagerProxy实例,它通过Binder发送命令“HANDLE_APPLICATION_CRASH_TRANSACTION”给ActivityManagerNative(实际上就是ActivityManagerService)的Server端,然后ActivityManagerService的handleApplicationCrash函数就会被调用。
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
ProcessRecord r = findAppProcess(app, "Crash");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
handleApplicationCrashInner("crash", r, processName, crashInfo);
}
这里面有两个参数,第一个参数,就是UncaughtHandler实例传进来的mApplicationObject,第二个参数是根据Throwable构建的一个ApplicationErrorReport.CrashInfo实例。第二个参数很容易懂,那么第一个参数代表啥意思呢?
第一节中讲应用启动时只说了一个片段,剩下的还要好多步骤。应用的进程创建了,只是创建了一个运行环境而已。在AMS调用Process.start的第一个参数是“android.app.ActivityThread”,在第11步中applicationInit会调用其main方法,main中会实例化一个ActivityThread,所以很多人说ActivityThread代表着App的主线程。ActivityThread中的attach会被调用,在该方法中会调用RuntimeInit.setApplicationObject(mAppThread.asBinder()),此时mAppThread.asBinder())的值被赋给了mApplicationObject,实际上它就是ApplicationThread,而ApplicationThread是作为App的Binder存在的,从某种意义上代表着ActivityThread。
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
ProcessRecord r = findAppProcess(app, "Crash");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
handleApplicationCrashInner("crash", r, processName, crashInfo);
}
ProcessRecord 代表着该应用的进程信息,在想zygote要求fork进程钱会创建它。
findAppProcess会根据ApplicationThread查找到对应的应用进程的记录。ApplicationThread的值是在完成应用进程的创建后,调用AMS的attachApplication是穿过来的,attachApplication会根据fork进程得到的应用的pid值,查找到ProcessRecord的实例,然后它的makeActive方法设置的。
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
UserHandle.getUserId(Binder.getCallingUid()), processName,
r == null ? -1 : r.info.flags,
crashInfo.exceptionClassName,
crashInfo.exceptionMessage,
crashInfo.throwFileName,
crashInfo.throwLineNumber);
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
mAppErrors.crashApplication(r, crashInfo);
}
- 将crash信息写入到EventLog中,eventlog保存在/system/etc/event-log-tag中。
- 将log记录到DropBox中,dropbox的log保存在/data/system/dropbox目录下,它的文件名以system_server或system_app或data_app打头,然后以_crash开始。
- 调用crashApplication继续后续的处理
crashApplication
void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
final long origId = Binder.clearCallingIdentity();
try {
crashApplicationInner(r, crashInfo);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
......
synchronized (mService) {
/**
* If crash is handled by instance of {@link android.app.IActivityController},
* finish now and don't show the app error dialog.
*/
// 处理有Controller的情况,Controller是一个测试的接口
if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
timeMillis)) {
return;
}
/**
* If this process was running instrumentation, finish now - it will be handled in
* {@link ActivityManagerService#handleAppDiedLocked}.
*/
if (r != null && r.instrumentationClass != null) {
return;
}
......
// 弹出crash的对话框
AppErrorDialog.Data data = new AppErrorDialog.Data();
...
mService.mUiHandler.sendMessage(msg);
}
// 等待用户选择对话框的结果
int res = result.get();
// 根据用户的选择处理不同的case
Intent appErrorIntent = null;
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
res = AppErrorDialog.FORCE_QUIT;
}
synchronized (mService) {
if (res == AppErrorDialog.MUTE) {
stopReportingCrashesLocked(r);
}
if (res == AppErrorDialog.RESTART) {
mService.removeProcessLocked(r, false, true, "crash");
if (task != null) {
try {
mService.startActivityFromRecents(task.taskId,
ActivityOptions.makeBasic().toBundle());
} catch (IllegalArgumentException e) {
// Hmm, that didn't work, app might have crashed before creating a
// recents entry. Let's see if we have a safe-to-restart intent.
final Set<String> cats = task.intent.getCategories();
if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
mService.startActivityInPackage(task.mCallingUid,
task.mCallingPackage, task.intent,
null, null, null, 0, 0,
ActivityOptions.makeBasic().toBundle(),
task.userId, null, null);
}
}
}
}
if (res == AppErrorDialog.FORCE_QUIT) {
long orig = Binder.clearCallingIdentity();
try {
// Kill it with fire!
mService.mStackSupervisor.handleAppCrashLocked(r);
if (!r.persistent) {
mService.removeProcessLocked(r, false, false, "crash");
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
} finally {
Binder.restoreCallingIdentity(orig);
}
}
if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
}
if (r != null && !r.isolated && res != AppErrorDialog.RESTART) {
// XXX Can't keep track of crash time for isolated processes,
// since they don't have a persistent identity.
mProcessCrashTimes.put(r.info.processName, r.uid,
SystemClock.uptimeMillis());
}
}
if (appErrorIntent != null) {
try {
mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "bug report receiver dissappeared", e);
}
}
}
这些主要是一些清理工作,另外如果在弹出的对话框中,选择上报,会发送Intent.ACTION_APP_ERROR启动一个上报的Activity。
这一大坨代码主要的作用就是做一些善后工作,并将应用进程杀死。
进程死后的善后
在应用进程fork后的attach步骤中,有类似如下的代码
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
......
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
startProcessLocked(app, "link fail", processName);
return false;
}
......
}
thread就是ApplicationThread类型的实例,这段代码向ApplicationThread注册了一个死亡通知,当app的进程被杀死后,AppDeathRecipient的binderDied函数将会被调用。
public void binderDied() {
if (DEBUG_ALL) Slog.v(
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true);
}
}
appDiedLocked做的工作主要是清理。在cleanUpApplicationRecordLocked函数中还会检查App的android:persistent是否设置了,如果设置了会重启该应用。
上一篇: iOS-定位程序崩溃位置
下一篇: ios网易大白Crash自动防护