理解Android Crash處理流程
基於Android 6.0的源碼剖析, 分析Android應用Crash是如何處理的。
/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java (含內部類AMP)
/frameworks/base/core/java/android/app/ApplicationErrorReport.java
/frameworks/base/services/core/java/com/android/server/
- am/ActivityManagerService.java
- am/ProcessRecord.java
- am/ActivityRecord.java
- am/ActivityStackSupervisor.java
- am/ActivityStack.java
- am/ActivityRecord.java
- am/BroadcastQueue.java
- wm/WindowManagerService.java
/libcore/libart/src/main/java/java/lang/Thread.java
一、概述
App crash(全稱Application crash), 對於Crash可分為native crash和framework crash(包含app crash在內),對於crash相信很多app開發者都會遇到,那麼上層什麼時候會出現crash呢,系統又是如何處理crash的呢。例如,在app大家經常使用try...catch
語句,那麼如果沒有有效catch exception,就是導致應用crash,發生沒有catch exception,系統便會來進行捕獲,並進入crash流程。如果你是從事Android系統開發或者架構相關工作,或者遇到需要解系統性的疑難雜症,那麼很有必要了解系統Crash處理流程,知其然還需知其所以然;如果你僅僅是App初級開發,可能本文並非很適合閱讀,整個系統流程錯綜複雜。
在Android系統啟動系列文章,已講述過上層應用都是由Zygote fork孵化而來,分為system_server系統進程和各種應用進程,在這些進程創建之初會設置未捕獲異常的處理器,當系統拋出未捕獲的異常時,最終都交給異常處理器。
- 對於system_server進程:文章Android系統啟動-SystemServer上篇,system_server啟動過程中由RuntimeInit.java的
commonInit
方法設置UncaughtHandler,用於處理未捕獲異常; - 對於普通應用進程:文章理解Android進程創建流程 ,進程創建過程中,同樣會調用RuntimeInit.java的
commonInit
方法設置UncaughtHandler。
1.1 crash調用鏈
crash流程的方法調用關係來結尾:
AMP.handleApplicationCrash
AMS.handleApplicationCrash
AMS.findAppProcess
AMS.handleApplicationCrashInner
AMS.addErrorToDropBox
AMS.crashApplication
AMS.makeAppCrashingLocked
AMS.startAppProblemLocked
ProcessRecord.stopFreezingAllLocked
ActivityRecord.stopFreezingScreenLocked
WMS.stopFreezingScreenLocked
WMS.stopFreezingDisplayLocked
AMS.handleAppCrashLocked
mUiHandler.sendMessage(SHOW_ERROR_MSG)
Process.killProcess(Process.myPid());
System.exit(10);
接下來說說這個過程。
二、Crash處理流程
那麼接下來以commonInit()方法為起點來展開說明。
1. RuntimeInit.commonInit
public class RuntimeInit {
...
private static final void commonInit() {
//設置默認的未捕獲異常處理器,UncaughtHandler實例化過程【見小節2】
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
...
}
}
setDefaultUncaughtExceptionHandler()只是將異常處理器handler對象賦給Thread成員變量,即Thread.defaultUncaughtHandler = new UncaughtHandler()
。接下來看看UncaughtHandler對象實例化過程。
2. UncaughtHandler
[–>RuntimeInit.java]
private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
//覆寫接口方法
public void uncaughtException(Thread t, Throwable e) {
try {
//保證crash處理過程不會重入
if (mCrashing) return;
mCrashing = true;
if (mApplicationObject == null) {
//system_server進程
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);
}
//啟動crash對話框,等待處理完成 【見小節2.1和3】
ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
} catch (Throwable t2) {
...
} finally {
//確保當前進程徹底殺掉【見小節11】
Process.killProcess(Process.myPid());
System.exit(10);
}
}
}
- 當system進程crash的信息:
- 開頭
*** FATAL EXCEPTION IN SYSTEM PROCESS [線程名]
; - 接著輸出發生crash時的調用棧信息;
- 開頭
- 當app進程crash時的信息:
- 開頭
FATAL EXCEPTION: [線程名]
; - 緊接著
Process: [進程名], PID: [進程id]
; - 最後輸出發生crash時的調用棧信息。
- 開頭
看到這裡,你就會發現要從log中搜索crash信息,只需要搜索關鍵詞FATAL EXCEPTION
;如果需要進一步篩選只搜索系統crash信息,則可以搜索的關鍵詞可以有多樣,比如*** FATAL EXCEPTION
。
當輸出完crash信息到logcat裡面,這只是crash流程的剛開始階段,接下來彈出crash對話框
,ActivityManagerNative.getDefault()返回的是ActivityManagerProxy(簡稱AMP
),AMP
經過binder調用最終交給ActivityManagerService(簡稱AMS
)中相應的方法去處理,故接下來調用的是AMS.handleApplicationCrash()。
注意: mApplicationObject等於null,一定不是普通的app進程. 但是除了system進程, 也有可能是shell進程, 即通過app_process + 命令參數 的方式創建的進程.
2.1 CrashInfo
[-> ApplicationErrorReport.java]
public class ApplicationErrorReport implements Parcelable {
...
public static class CrashInfo {
public CrashInfo(Throwable tr) {
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 256);
tr.printStackTrace(pw); //輸出棧trace
pw.flush();
stackTrace = sw.toString();
exceptionMessage = tr.getMessage();
Throwable rootTr = tr;
while (tr.getCause() != null) {
tr = tr.getCause();
if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) {
rootTr = tr;
}
String msg = tr.getMessage();
if (msg != null && msg.length() > 0) {
exceptionMessage = msg;
}
}
exceptionClassName = rootTr.getClass().getName();
if (rootTr.getStackTrace().length > 0) {
StackTraceElement trace = rootTr.getStackTrace()[0];
throwFileName = trace.getFileName();
throwClassName = trace.getClassName();
throwMethodName = trace.getMethodName();
throwLineNumber = trace.getLineNumber();
} else {
throwFileName = "unknown";
throwClassName = "unknown";
throwMethodName = "unknown";
throwLineNumber = 0;
}
}
...
}
}
將crash信息文件名
,類名
,方法名
,對應行號
以及異常信息
都封裝到CrashInfo對象。
3. handleApplicationCrash
[–>ActivityManagerService.java]
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
//獲取進程record對象【見小節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<ProcessRecord>();
對於代碼mProcessNames.getMap()
返回的是mMap
,而mMap= new ArrayMap<String, SparseArray<ProcessRecord>>()
;
知識延伸: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。下面看看其get()和put()方法
//獲取mMap中(name,uid)所對應的ProcessRecord
public ProcessRecord get(String name, int uid) {};
//將(name,uid, value)添加到mMap
public ProcessRecord put(String name, int uid, ProcessRecord value) {};
findAppProcess()根據app(IBinder類型)來查詢相應的目標對象ProcessRecord。
有了進程記錄對象ProcessRecord和進程名processName,則進入執行Crash處理方法,繼續往下看。
4. handleApplicationCrashInner
[–>ActivityManagerService.java]
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
//將Crash信息寫入到Event log
EventLog.writeEvent(EventLogTags.AM_CRASH,...);
//將錯誤信息添加到DropBox
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
//【見小節5】
crashApplication(r, crashInfo);
}
其中addErrorToDropBox是將crash的信息輸出到目錄/data/system/dropbox
。例如system_server的dropbox文件名為system_server_crash@xxx.txt (xxx代表的是時間戳)
5. crashApplication
[–>ActivityManagerService.java]
private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
long timeMillis = System.currentTimeMillis();
String shortMsg = crashInfo.exceptionClassName;
String longMsg = crashInfo.exceptionMessage;
String stackTrace = crashInfo.stackTrace;
if (shortMsg != null && longMsg != null) {
longMsg = shortMsg + ": " + longMsg;
} else if (shortMsg != null) {
longMsg = shortMsg;
}
AppErrorResult result = new AppErrorResult();
synchronized (this) {
// 當存在ActivityController的情況,比如monkey
if (mController != null) {
try {
String name = r != null ? r.processName : null;
int pid = r != null ? r.pid : Binder.getCallingPid();
int uid = r != null ? r.info.uid : Binder.getCallingUid();
//調用monkey的appCrashed
if (!mController.appCrashed(name, pid,
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
&& "Native crash".equals(crashInfo.exceptionClassName)) {
Slog.w(TAG, "Skip killing native crashed app " + name
+ "(" + pid + ") during testing");
} else {
Slog.w(TAG, "Force-killing crashed app " + name
+ " at watcher's request");
if (r != null) {
r.kill("crash", true);
} else {
Process.killProcess(pid);
killProcessGroup(uid, pid);
}
}
return;
}
} catch (RemoteException e) {
mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
//清除遠程調用者uid和pid信息,並保存到origId
final long origId = Binder.clearCallingIdentity();
...
//【見小節6】
if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {
Binder.restoreCallingIdentity(origId);
return;
}
Message msg = Message.obtain();
msg.what = SHOW_ERROR_MSG;
HashMap data = new HashMap();
data.put("result", result);
data.put("app", r);
msg.obj = data;
//發送消息SHOW_ERROR_MSG,彈出提示crash的對話框,等待用戶選擇【見小節10】
mUiHandler.sendMessage(msg);
//恢復遠程調用者uid和pid
Binder.restoreCallingIdentity(origId);
}
//進入阻塞等待,直到用戶選擇crash對話框"退出"或者"退出並報告"
int res = result.get();
Intent appErrorIntent = null;
synchronized (this) {
if (r != null && !r.isolated) {
// 將崩潰的進程信息保存到mProcessCrashTimes
mProcessCrashTimes.put(r.info.processName, r.uid,
SystemClock.uptimeMillis());
}
if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
//創建action="android.intent.action.APP_ERROR",組件為r.errorReportReceiver的Intent
appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
}
}
if (appErrorIntent != null) {
try {
//啟動Intent為appErrorIntent的Activity
mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "bug report receiver dissappeared", e);
}
}
}
該方法主要做的兩件事:
- 調用
makeAppCrashingLocked
,繼續處理crash流程; - 發送消息SHOW_ERROR_MSG,彈出提示crash的對話框,等待用戶選擇;
6. makeAppCrashingLocked
[–>ActivityManagerService.java]
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace) {
app.crashing = true;
//封裝crash信息到crashingReport對象
app.crashingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
//【見小節7】
startAppProblemLocked(app);
//停止屏幕凍結【見小節8】
app.stopFreezingAllLocked();
//【見小節9】
return handleAppCrashLocked(app, "force-crash", shortMsg, longMsg, stackTrace);
}
7. startAppProblemLocked
[–>ActivityManagerService.java]
void startAppProblemLocked(ProcessRecord app) {
app.errorReportReceiver = null;
for (int userId : mCurrentProfileIds) {
if (app.userId == userId) {
//獲取當前用戶下的crash應用的error receiver【見小節7.1】
app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mContext, app.info.packageName, app.info.flags);
}
}
//忽略當前app的廣播接收【見小節7.2】
skipCurrentReceiverLocked(app);
}
該方法主要功能:
- 獲取當前用戶下的crash應用的error receiver;
- 忽略當前app的廣播接收;
7.1 getErrorReportReceiver
[-> ApplicationErrorReport.java]
public static ComponentName getErrorReportReceiver(Context context,
String packageName, int appFlags) {
//檢查Settings中的"send_action_app_error"是否使能錯誤報告的功能
int enabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SEND_ACTION_APP_ERROR, 0);
if (enabled == 0) {
//1.當未使能時,則直接返回
return null;
}
PackageManager pm = context.getPackageManager();
String candidate = null;
ComponentName result = null;
try {
//獲取該crash應用的安裝器的包名
candidate = pm.getInstallerPackageName(packageName);
} catch (IllegalArgumentException e) {
}
if (candidate != null) {
result = getErrorReportReceiver(pm, packageName, candidate);//【見下文】
if (result != null) {
//2.當找到該crash應用的安裝器,則返回;
return result;
}
}
if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
//該系統屬性名為"ro.error.receiver.system.apps"
candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
result = getErrorReportReceiver(pm, packageName, candidate);//【見下文】
if (result != null) {
//3.當crash應用是系統應用時,且系統屬性指定error receiver時,則返回;
return result;
}
}
//該默認屬性名為"ro.error.receiver.default"
candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
//4.當默認屬性值指定error receiver時,則返回;
return getErrorReportReceiver(pm, packageName, candidate); //【見下文】
}
getErrorReportReceiver:這是同名不同輸入參數的另一個方法:
static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
String receiverPackage) {
if (receiverPackage == null || receiverPackage.length() == 0) {
return null;
}
//當安裝應用程序的安裝器Crash,則直接返回
if (receiverPackage.equals(errorPackage)) {
return null;
}
//ACTION_APP_ERROR值為"android.intent.action.APP_ERROR"
Intent intent = new Intent(Intent.ACTION_APP_ERROR);
intent.setPackage(receiverPackage);
ResolveInfo info = pm.resolveActivity(intent, 0);
if (info == null || info.activityInfo == null) {
return null;
}
//創建包名為receiverPackage的組件
return new ComponentName(receiverPackage, info.activityInfo.name);
}
7.2 skipCurrentReceiverLocked
[–>ActivityManagerService.java]
void skipCurrentReceiverLocked(ProcessRecord app) {
for (BroadcastQueue queue : mBroadcastQueues) {
queue.skipCurrentReceiverLocked(app); //【見小節7.2.1】
}
}
7.2.1 skipCurrentReceiverLocked
[-> BroadcastQueue.java]
public void skipCurrentReceiverLocked(ProcessRecord app) {
BroadcastRecord r = null;
//查看app進程中的廣播
if (mOrderedBroadcasts.size() > 0) {
BroadcastRecord br = mOrderedBroadcasts.get(0);
if (br.curApp == app) {
r = br;
}
}
if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
r = mPendingBroadcast;
}
if (r != null) {
//結束app進程的廣播結束
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
//廣播調度
scheduleBroadcastsLocked();
}
}
8. PR.stopFreezingAllLocked
[-> ProcessRecord.java]
public void stopFreezingAllLocked() {
int i = activities.size();
while (i > 0) {
i--;
activities.get(i).stopFreezingScreenLocked(true); //【見小節8.1】
}
}
其中activities類型為ArrayList<ActivityRecord
>,停止進程裡所有的Activity
8.1. AR.stopFreezingScreenLocked
[-> ActivityRecord.java]
public void stopFreezingScreenLocked(boolean force) {
if (force || frozenBeforeDestroy) {
frozenBeforeDestroy = false;
//mWindowManager類型為WMS //【見小節8.1.1】
service.mWindowManager.stopAppFreezingScreen(appToken, force);
}
}
其中appToken是IApplication.Stub類型,即WindowManager的token。
8.1.1 WMS.stopFreezingScreenLocked
[-> WindowManagerService.java]
@Override
public void stopFreezingScreen() {
//權限檢查
if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
"stopFreezingScreen()")) {
throw new SecurityException("Requires FREEZE_SCREEN permission");
}
synchronized(mWindowMap) {
if (mClientFreezingScreen) {
mClientFreezingScreen = false;
mLastFinishedFreezeSource = "client";
final long origId = Binder.clearCallingIdentity();
try {
stopFreezingDisplayLocked(); //【見流程8.1.1.1】
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
}
8.1.1.1 WMS.stopFreezingDisplayLocked
[-> WindowManagerService.java]
private void stopFreezingDisplayLocked() {
if (!mDisplayFrozen) {
return; //顯示沒有凍結,則直接返回
}
//往往跟屏幕旋轉相關
...
mDisplayFrozen = false;
//從上次凍屏到現在的總時長
mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);
//移除凍屏的超時消息
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
boolean updateRotation = false;
//獲取默認的DisplayContent
final DisplayContent displayContent = getDefaultDisplayContentLocked();
final int displayId = displayContent.getDisplayId();
ScreenRotationAnimation screenRotationAnimation =
mAnimator.getScreenRotationAnimationLocked(displayId);
//屏幕旋轉動畫的相關操作
if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
&& screenRotationAnimation.hasScreenshot()) {
DisplayInfo displayInfo = displayContent.getDisplayInfo();
boolean isDimming = displayContent.isDimming();
if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
mExitAnimId = mEnterAnimId = 0;
}
//加載動畫最大時長為10s
if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
scheduleAnimationLocked();
} else {
screenRotationAnimation.kill();
mAnimator.setScreenRotationAnimationLocked(displayId, null);
updateRotation = true;
}
} else {
if (screenRotationAnimation != null) {
screenRotationAnimation.kill();
mAnimator.setScreenRotationAnimationLocked(displayId, null);
}
updateRotation = true;
}
//經過層層調用到InputManagerService服務,IMS服務使能輸入事件分發功能
mInputMonitor.thawInputDispatchingLw();
boolean configChanged;
//當display被凍結時不再計算屏幕方向,以避免不連續的狀態。
configChanged = updateOrientationFromAppTokensLocked(false);
//display凍結時,執行gc操作
mH.removeMessages(H.FORCE_GC);
mH.sendEmptyMessageDelayed(H.FORCE_GC, 2000);
//mScreenFrozenLock的類型為PowerManager.WakeLock,即釋放屏幕凍結的鎖
mScreenFrozenLock.release();
if (updateRotation) {
//更新當前的屏幕方向
configChanged |= updateRotationUncheckedLocked(false);
}
if (configChanged) {
//向mH發送configuraion改變的消息
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
}
該方法主要功能:
- 處理屏幕旋轉相關邏輯;
- 移除凍屏的超時消息;
- 屏幕旋轉動畫的相關操作;
- 使能輸入事件分發功能;
- display凍結時,執行gc操作;
- 更新當前的屏幕方向;
- 向mH發送configuraion改變的消息。
9.AMS.handleAppCrashLocked
[-> ActivityManagerService.java]
private boolean handleAppCrashLocked(ProcessRecord app, String reason,
String shortMsg, String longMsg, String stackTrace) {
long now = SystemClock.uptimeMillis();
Long crashTime;
if (!app.isolated) {
crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
} else {
crashTime = null;
}
//當同一個進程,連續兩次crash的時間間隔小於1分鐘時,則認為crash太過於頻繁
if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.userId, app.info.processName, app.uid);
//【見小節9.1】
mStackSupervisor.handleAppCrashLocked(app);
if (!app.persistent) {
//不再重啟非persistent進程,除非用戶顯式地調用
EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
app.info.processName);
if (!app.isolated) {
//將當前app加入到mBadProcesses
mBadProcesses.put(app.info.processName, app.uid,
new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
mProcessCrashTimes.remove(app.info.processName, app.uid);
}
app.bad = true;
app.removed = true;
//移除進程的所有服務,保證不再重啟【見小節9.2】
removeProcessLocked(app, false, false, "crash");
//恢復最頂部的Activity【見小節9.3】
mStackSupervisor.resumeTopActivitiesLocked();
return false;
}
mStackSupervisor.resumeTopActivitiesLocked();
} else {
//此處reason="force-crash"【見小節9.4】
mStackSupervisor.finishTopRunningActivityLocked(app, reason);
}
//運行在當前進程中的所有服務的crash次數執行加1操作
for (int i=app.services.size()-1; i>=0; i--) {
ServiceRecord sr = app.services.valueAt(i);
sr.crashCount++;
}
//當桌面應用crash,並且被三方app所取代,那麼需要清空桌面應用的偏愛選項。
final ArrayList<ActivityRecord> activities = app.activities;
if (app == mHomeProcess && activities.size() > 0
&& (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.isHomeActivity()) {
//清空偏愛應用
ActivityThread.getPackageManager()
.clearPackagePreferredActivities(r.packageName);
}
}
}
if (!app.isolated) {
//無法記錄孤立進程的crash時間點,由於他們並沒有一個固定身份
mProcessCrashTimes.put(app.info.processName, app.uid, now);
}
//當app存在crash的handler,那麼交給其處理
if (app.crashHandler != null) mHandler.post(app.crashHandler);
return true;
}
- 當同一進程在時間間隔小於1分鐘時連續兩次crash,則執行的情況下:
- 對於非persistent進程:
- [9.1] mStackSupervisor.handleAppCrashLocked(app);
- [9.2] removeProcessLocked(app, false, false, “crash”);
- [9.3] mStackSupervisor.resumeTopActivitiesLocked();
- 對於persistent進程,則只執行
- [9.3] mStackSupervisor.resumeTopActivitiesLocked();
- 對於非persistent進程:
- 否則執行
- [9.4] mStackSupervisor.finishTopRunningActivityLocked(app, reason);
9.1 ASS.handleAppCrashLocked
[-> ActivityStackSupervisor.java]
void handleAppCrashLocked(ProcessRecord app) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
int stackNdx = stacks.size() - 1;
while (stackNdx >= 0) {
//調用ActivityStack【見小節9.1.1】
stacks.get(stackNdx).handleAppCrashLocked(app);
stackNdx--;
}
}
}
9.1.1 AS.handleAppCrashLocked
[-> ActivityStack.java]
void handleAppCrashLocked(ProcessRecord app) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.app == app) {
r.app = null;
//結束當前activity
finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
}
}
}
}
這裡的mTaskHistory
數據類型為ArrayList
9.2 AMS.removeProcessLocked
[-> ActivityManagerService.java]
private final boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
final int uid = app.uid;
//從mProcessNames移除該進程
removeProcessNameLocked(name, uid);
...
if (app.pid > 0 && app.pid != MY_PID) {
int pid = app.pid;
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(pid); //移除該pid
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
...
boolean willRestart = false;
//對於非孤立的persistent進程設置成可重啟flags
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
willRestart = true;
} else {
needRestart = true;
}
}
// 殺進程【9.2.1】
app.kill(reason, true);
//移除進程並清空該進程相關聯的activity/service等組件 【9.2.2】
handleAppDiedLocked(app, willRestart, allowRestart);
if (willRestart) {
//此處willRestart=false,不進入該分支
removeLruProcessLocked(app);
addAppLocked(app.info, false, null /* ABI override */);
}
} else {
mRemovedProcesses.add(app);
}
return needRestart;
}
mProcessNames
數據類型為ProcessMap,這是以進程名為key,記錄著所有的ProcessRecord信息 mPidsSelfLocked
數據類型為SparseArray,這是以pid為key,記錄著所有的ProcessRecord信息。該對象的同步保護是通過自身鎖,而非全局ActivityManager鎖。
9.2.1 app.kill
[-> ProcessRecord.java]
void kill(String reason, boolean noisy) {
if (!killedByAm) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
if (noisy) {
Slog.i(TAG, "Killing " + toShortString() + " (adj " + setAdj + "): " + reason);
}
EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
Process.killProcessQuiet(pid); //殺進程
Process.killProcessGroup(info.uid, pid); //殺進程組,包括native進程
if (!persistent) {
killed = true;
killedByAm = true;
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
此處reason為“crash”,關於殺進程的過程見我的另一篇文章理解殺進程的實現原理.
9.2.2 handleAppDiedLocked
[-> ActivityManagerService.java]
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
//清除應用中service/receiver/ContentProvider信息
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
if (!kept && !restarting) {
removeLruProcessLocked(app);
if (pid > 0) {
ProcessList.remove(pid);
}
}
if (mProfileProc == app) {
clearProfilerLocked();
}
//清除應用中activity相關信息
boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
app.activities.clear();
...
if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
}
9.3 ASS.resumeTopActivitiesLocked
[-> ActivityStackSupervisor.java]
boolean resumeTopActivitiesLocked() {
return resumeTopActivitiesLocked(null, null, null);
}
boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
Bundle targetOptions) {
if (targetStack == null) {
targetStack = mFocusedStack;
}
boolean result = false;
if (isFrontStack(targetStack)) {
//【見小節9.3.1】
result = targetStack.resumeTopActivityLocked(target, targetOptions);
}
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack == targetStack) {
continue; //已經啟動
}
if (isFrontStack(stack)) {
stack.resumeTopActivityLocked(null);
}
}
}
return result;
}
此處mFocusedStack
是當前正在等待接收input事件或者正在啟動下一個activity的ActivityStack
。
9.3.1 AS.resumeTopActivityLocked
[-> ActivityStack.java]
final boolean .resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
...
result = resumeTopActivityInnerLocked(prev, options);//【見小節9.3.2】
return result;
}
9.3.2 AS.resumeTopActivityInnerLocked
[-> ActivityStack.java]
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
//找到mTaskHistory棧中第一個未處於finishing狀態的Activity
final ActivityRecord next = topRunningActivityLocked(null);
if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
mStackSupervisor.allResumedActivitiesComplete()) {
//當top activity已經處於resume,則無需操作;
return false;
}
if (mService.isSleepingOrShuttingDown()
&& mLastPausedActivity == next
&& mStackSupervisor.allPausedActivitiesComplete()) {
//當正處於sleeping狀態,top activity處於paused,則無需操作
return false;
}
//正在啟動app的activity,確保app不會被設置為stopped
AppGlobals.getPackageManager().setPackageStoppedState(
next.packageName, false, next.userId);
//回調應用onResume方法
next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
mService.isNextTransitionForward(), resumeAnimOptions);
...
}
該方法代碼比較長,這裡就簡單列舉幾條比較重要的代碼。執行完該方法,應用也便完成了activity的resume過程。
9.4 finishTopRunningActivityLocked
9.4.1 ASS.finishTopRunningActivityLocked
[-> ActivityStackSupervisor.java]
void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
//此處reason= "force-crash"【見小節9.4.2】
stack.finishTopRunningActivityLocked(app, reason);
}
}
}
9.4.2 AS.finishTopRunningActivityLocked
final void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
//找到棧頂第一個不處於finishing狀態的activity
ActivityRecord r = topRunningActivityLocked(null);
if (r != null && r.app == app) {
int taskNdx = mTaskHistory.indexOf(r.task);
int activityNdx = r.task.mActivities.indexOf(r);
//【見小節9.4.3】
finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
--activityNdx;
if (activityNdx < 0) {
do {
--taskNdx;
if (taskNdx < 0) {
break;
}
activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
} while (activityNdx < 0);
}
if (activityNdx >= 0) {
r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
if (r.state == ActivityState.RESUMED
|| r.state == ActivityState.PAUSING
|| r.state == ActivityState.PAUSED) {
if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
//【見小節9.4.3】
finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
}
}
}
}
}
9.4.3 AS.finishActivityLocked
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj) {
if (r.finishing) {
return false; //正在finishing則返回
}
//設置finish狀態的activity不可見
r.makeFinishingLocked();
//暫停key的分發事件
r.pauseKeyDispatchingLocked();
mWindowManager.prepareAppTransition(endTask
? AppTransition.TRANSIT_TASK_CLOSE
: AppTransition.TRANSIT_ACTIVITY_CLOSE, false);
mWindowManager.setAppVisibility(r.appToken, false);
//回調activity的onPause方法
startPausingLocked(false, false, false, false);
...
}
該方法最終會回調到activity的pause方法。
執行到這,我們還回過來看小節5.crashApplication
中,處理完makeAppCrashingLocked,則會再發送消息SHOW_ERROR_MSG,彈出提示crash的對話框,接下來再看看該過程。
10. UiHandler
通過mUiHandler發送message,且消息的msg.waht=SHOW_ERROR_MSG,接下來進入UiHandler來看看handleMessage的處理過程。
[-> ActivityManagerService.java]
final class UiHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ERROR_MSG: {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
synchronized (ActivityManagerService.this) {
ProcessRecord proc = (ProcessRecord)data.get("app");
AppErrorResult res = (AppErrorResult) data.get("result");
、
boolean isBackground = (UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID
&& proc.pid != MY_PID);
...
if (mShowDialogs && !mSleeping && !mShuttingDown) {
//創建提示crash對話框,等待用戶選擇,5分鐘操作等待。
Dialog d = new AppErrorDialog(mContext,
ActivityManagerService.this, res, proc);
d.show();
proc.crashDialog = d;
} else {
//當處於sleep狀態,則默認選擇退出。
if (res != null) {
res.set(0);
}
}
}
} break;
...
}
}
在發生crash時,默認系統會彈出提示crash的對話框,並阻塞等待用戶選擇是“退出”或 “退出並報告”,當用戶不做任何選擇時5min超時後,默認選擇“退出”,當手機休眠時也默認選擇“退出”。到這裡也並沒有真正結束,在小節2.uncaughtException
中在finnally
語句塊還有一個殺進程的動作。
11. killProcess
Process.killProcess(Process.myPid());
System.exit(10);
通過finnally語句塊保證能執行並徹底殺掉Crash進程,關於殺進程的過程見我的另一篇文章理解殺進程的實現原理.。當Crash進程被殺後,並沒有完全結束,還有Binder死亡通知的流程還沒有處理完成。
12. 小結
當進程拋出未捕獲異常時,則系統會處理該異常並進入crash處理流程。
其中最為核心的工作圖中紅色部分AMS.handleAppCrashLocked
的主要功能:
- 當同一進程1分鐘之內連續兩次crash,則執行的情況下:
- 對於非persistent進程:
- ASS.handleAppCrashLocked, 直接結束該應用所有activity
- AMS.removeProcessLocked,殺死該進程以及同一個進程組下的所有進
- ASS.resumeTopActivitiesLocked,恢復棧頂第一個非finishing狀態的activity
- 對於persistent進程,則只執行
- ASS.resumeTopActivitiesLocked,恢復棧頂第一個非finishing狀態的activity
- 對於非persistent進程:
- 否則,當進程沒連續頻繁crash
- ASS.finishTopRunningActivityLocked,執行結束棧頂正在運行activity
另外,AMS.handleAppCrashLocked
,該方法內部主要調用鏈,如下:
AMS.handleAppCrashLocked
ASS.handleAppCrashLocked
AS.handleAppCrashLocked
AS.finishCurrentActivityLocked
AMS.removeProcessLocked
ProcessRecord.kill
AMS.handleAppDiedLocked
ASS.handleAppDiedLocked
AMS.cleanUpApplicationRecordLocked
AS.handleAppDiedLocked
AS.removeHistoryRecordsForAppLocked
ASS.resumeTopActivitiesLocked
AS.resumeTopActivityLocked
AS.resumeTopActivityInnerLocked
ASS.finishTopRunningActivityLocked
AS.finishTopRunningActivityLocked
AS.finishActivityLocked
三、Binder死亡通知
進程被殺,如果還記得Binder的死亡回調機制,在應用進程創建的過程中有一個attachApplicationLocked
方法的過程中便會創建死亡通知。
[-> ActivityManagerService.java]
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
try {
//創建binder死亡通知
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;
}
...
}
當binder服務端掛了之後,便會通過binder的DeathRecipient來通知AMS進行相應的清理收尾工作。前面已經降到crash的進程會被kill掉,那麼當該進程會殺,則會回調到binderDied()方法。
1. binderDied
[-> ActivityManagerService.java]
private final class AppDeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true);//【見小節2】
}
}
}
2. appDiedLocked
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
boolean fromBinderDied) {
...
if (!app.killed) {
if (!fromBinderDied) {
Process.killProcessQuiet(pid);
}
killProcessGroup(app.info.uid, pid);
app.killed = true;
}
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
boolean doLowMem = app.instrumentationClass == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) {
mAllowLowerMemLevel = true;
} else {
mAllowLowerMemLevel = false;
doLowMem = false;
}
//【見小節3】
handleAppDiedLocked(app, false, true);
if (doOomAdj) {
updateOomAdjLocked();
}
if (doLowMem) {
doLowMemReportIfNeededLocked(app);
}
}
...
}
3 handleAppDiedLocked
[-> ActivityManagerService.java]
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
//清理應用程序service, BroadcastReceiver, ContentProvider相關信息【見小節4】
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
if (!kept && !restarting) {
removeLruProcessLocked(app);
if (pid > 0) {
ProcessList.remove(pid);
}
}
//清理activity相關信息
boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
app.activities.clear();
...
//恢復棧頂第一個非finish的activity
if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
}
4 cleanUpApplicationRecordLocked
該方法清理應用程序service, BroadcastReceiver, ContentProvider,process相關信息,為了便於說明將該方法劃分為4個部分講解
4.1 清理service
參數restarting = false, allowRestart =true, index =-1
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index) {
...
mProcessesToGc.remove(app);
mPendingPssProcesses.remove(app);
//如果存在,則清除crash/anr/wait對話框
if (app.crashDialog != null && !app.forceCrashReport) {
app.crashDialog.dismiss();
app.crashDialog = null;
}
if (app.anrDialog != null) {
app.anrDialog.dismiss();
app.anrDialog = null;
}
if (app.waitDialog != null) {
app.waitDialog.dismiss();
app.waitDialog = null;
}
app.crashing = false;
app.notResponding = false;
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient(); //解除app的死亡通告
app.makeInactive(mProcessStats);
app.waitingToKill = null;
app.forcingToForeground = null;
//將app移除前臺進程
updateProcessForegroundLocked(app, false, false);
app.foregroundActivities = false;
app.hasShownUi = false;
app.treatLikeActivity = false;
app.hasAboveClient = false;
app.hasClientActivities = false;
//清理service信息,這個過程也比較複雜,後續再展開
mServices.killServicesLocked(app, allowRestart);
boolean restart = false;
}
- mProcessesToGc:記錄著需要儘快執行gc的進程列表
- mPendingPssProcesses:記錄著需要收集內存信息的進程列表
4.2 清理ContentProvider
private final boolean cleanUpApplicationRecordLocked(...) {
...
for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
//獲取該進程已發表的ContentProvider
ContentProviderRecord cpr = app.pubProviders.valueAt(i);
final boolean always = app.bad || !allowRestart;
//ContentProvider服務端被殺,則client端進程也會被殺
boolean inLaunching = removeDyingProviderLocked(app, cpr, always);
if ((inLaunching || always) && cpr.hasConnectionOrHandle()) {
restart = true; //需要重啟
}
cpr.provider = null;
cpr.proc = null;
}
app.pubProviders.clear();
//處理正在啟動並且是有client端正在等待的ContentProvider
if (cleanupAppInLaunchingProvidersLocked(app, false)) {
restart = true;
}
//取消已連接的ContentProvider的註冊
if (!app.conProviders.isEmpty()) {
for (int i = app.conProviders.size() - 1; i >= 0; i--) {
ContentProviderConnection conn = app.conProviders.get(i);
conn.provider.connections.remove(conn);
stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
conn.provider.name);
}
app.conProviders.clear();
}
4.3 清理BroadcastReceiver
private final boolean cleanUpApplicationRecordLocked(...) {
...
skipCurrentReceiverLocked(app);
// 取消註冊的廣播接收者
for (int i = app.receivers.size() - 1; i >= 0; i--) {
removeReceiverLocked(app.receivers.valueAt(i));
}
app.receivers.clear();
}
4.4 清理Process
private final boolean cleanUpApplicationRecordLocked(...) {
...
//當app正在備份時的處理方式
if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
...
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
bm.agentDisconnected(app.info.packageName);
}
for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
ProcessChangeItem item = mPendingProcessChanges.get(i);
if (item.pid == app.pid) {
mPendingProcessChanges.remove(i);
mAvailProcessChanges.add(item);
}
}
mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
if (!app.persistent || app.isolated) {
removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
} else if (!app.removed) {
//對於persistent應用,則需要重啟
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
//mProcessesOnHold:記錄著試圖在系統ready之前就啟動的進程。
//在那時並不啟動這些進程,先記錄下來,等系統啟動完成則啟動這些進程。
mProcessesOnHold.remove(app);
if (app == mHomeProcess) {
mHomeProcess = null;
}
if (app == mPreviousProcess) {
mPreviousProcess = null;
}
if (restart && !app.isolated) {
//仍有組件需要運行在該進程中,因此重啟該進程
if (index < 0) {
ProcessList.remove(app.pid);
}
addProcessNameLocked(app);
startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
//移除該進程相關信息
boolean removed;
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
app.setPid(0);
}
return false;
}
對於需要重啟進程的情形有:
mLaunchingProviders
:記錄著存在client端等待的ContentProvider。應用當前正在啟動中,當ContentProvider一旦發佈則將該ContentProvider將從該list去除。當進程包含這樣的ContentProvider,則需要重啟進程。mPersistentStartingProcesses
:記錄著試圖在系統ready之前就啟動的進程。在那時並不啟動這些進程,先記錄下來,等系統啟動完成則啟動這些進程。當進程屬於這種類型也需要重啟。
5. 小結
當crash進程執行kill操作後,進程被殺。此時需要掌握binder 死亡通知原理,由於Crash進程中擁有一個Binder服務端ApplicationThread
,而應用進程在創建過程調用attachApplicationLocked(),從而attach到system_server進程,在system_server進程內有一個ApplicationThreadProxy
,這是相對應的Binder客戶端。當Binder服務端ApplicationThread
所在進程(即Crash進程)掛掉後,則Binder客戶端能收到相應的死亡通知,從而進入binderDied流程。更多關於bInder原理,這裡就不細說,博客中有關於binder系列的專題。
四、 總結
本文主要以源碼的視角,詳細介紹了到應用crash後系統的處理流程:
- 首先發生crash所在進程,在創建之初便準備好了defaultUncaughtHandler,用來來處理Uncaught Exception,並輸出當前crash基本信息;
- 調用當前進程中的AMP.handleApplicationCrash;經過binder ipc機制,傳遞到system_server進程;
- 接下來,進入system_server進程,調用binder服務端執行AMS.handleApplicationCrash;
- 從
mProcessNames
查找到目標進程的ProcessRecord對象;並將進程crash信息輸出到目錄/data/system/dropbox
; - 執行makeAppCrashingLocked
- 創建當前用戶下的crash應用的error receiver,並忽略當前應用的廣播;
- 停止當前進程中所有activity中的WMS的凍結屏幕消息,並執行相關一些屏幕相關操作;
- 再執行handleAppCrashLocked方法,
- 當1分鐘內同一進程
連續crash兩次
時,且非persistent
進程,則直接結束該應用所有activity,並殺死該進程以及同一個進程組下的所有進程。然後再恢復棧頂第一個非finishing狀態的activity; - 當1分鐘內同一進程
連續crash兩次
時,且persistent
進程,,則只執行恢復棧頂第一個非finishing狀態的activity; - 當1分鐘內同一進程
未發生連續crash兩次
時,則執行結束棧頂正在運行activity的流程。
- 當1分鐘內同一進程
- 通過mUiHandler發送消息
SHOW_ERROR_MSG
,彈出crash對話框; - 到此,system_server進程執行完成。回到crash進程開始執行殺掉當前進程的操作;
- 當crash進程被殺,通過binder死亡通知,告知system_server進程來執行appDiedLocked();
- 最後,執行清理應用相關的activity/service/ContentProvider/receiver組件信息。
這基本就是整個應用Crash後系統的執行過程。 最後,再說說對於同一個app連續crash的情況:
- 當60s內連續crash兩次的非persistent進程時,被認定為bad進程:那麼如果第3次從後臺啟動該進程(Intent.getFlags來判斷),則會拒絕創建進程;
- 當crash次數達到兩次的非persistent進程時,則再次殺該進程,即便允許自啟的service也會在被殺後拒絕再次啟動。