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是否設置了,如果設置了會重啓該應用。