Jave Crash 处理流程
[基于 android P]
一、概述
App crash(全称Application crash), 对于Crash可分为native crash和java crash,对于crash相信很多app开发者都会遇到,那么上层什么时候会出现crash呢,系统又是如何处理crash的呢。例如,在app大家经常使用try…catch语句,那么如果没有有效catch exception,就是导致应用crash,系统便会来捕获,并进入crash流程。
我们知道上层应用进程都是由Zygote fork而来,分为system_server系统进程和各种应用进程,在这些进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时,最终都交给异常处理器,即RuntimeInit.java的commonInit方法设置UncaughtHandler来捕获异常。
crash处理流程的堆栈调用关系:
RuntimeInit.setDefaultUncaughtExceptionHandler
RuntimeInit.KillApplicationHandler.uncaughtException
AM.getService().handleApplicationCrash(binder通信)
AMS.handleApplicationCrash
AMS.findAppProcess
AMS.handleApplicationCrashInner
AMS.addErrorToDropBox
AppErr.crashApplication
AppErr.crashApplicationInner
AMS.makeAppCrashingLocked
AMS.startAppProblemLocked
ProcessRecord.stopFreezingAllLocked
ActivityRecord.stopFreezingScreenLocked
WMS.stopFreezingDisplayLocked
AMS.handleAppCrashLocked
mUiHandler.sendMessage(SHOW_ERROR_UI_MSG)
Process.killProcess(Process.myPid());
System.exit(10);
二、Crash处理流程
那么接下来以commonInit()方法为起点来梳理流程。
1. RuntimeInit.commonInit
public class RuntimeInit {
...
private static final void commonInit() {
//设置默认的未捕获异常处理器,UncaughtHandler实例化过程
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
...
}
}
setDefaultUncaughtExceptionHandler()只是将异常处理器handler对象赋给Thread成员变量,接下来看看KillApplicationHandler对象实例化过程。
2. UncaughtHandler
[–>RuntimeInit.java]
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler{
//覆写接口方法
public void uncaughtException(Thread t, Throwable e) {
try {
ensureLogging(t, e);//【2.1】
......
// 【3】
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
} catch (Throwable t2) {
......
} finally {
// 确保进程被杀死【11】
Process.killProcess(Process.myPid());
System.exit(10);
}
}
2.1 ensureLogging
[–> RuntimeInit.java]
private void ensureLogging(Thread t, Throwable e) {
if (!mLoggingHandler.mTriggered) {
try {
//【2.2】
mLoggingHandler.uncaughtException(t, e);
} catch (Throwable loggingThrowable) {
// Ignored.
}
}
}
2.2 uncaughtException
[-> RuntimeInit.LoggingHandler]
private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
public volatile boolean mTriggered = false;
@Override
public void uncaughtException(Thread t, Throwable e) {
mTriggered = true;
if (mCrashing) return;
//注意: mApplicationObject等于null,一定不是普通的app进程.
//除了system进程, 也有可能是shell进程, 即通过app_process + 命令参数 的方式创建的进程。
if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
// The "FATAL EXCEPTION" string is still used on Android even though
// apps can set a custom UncaughtExceptionHandler that renders uncaught
// exceptions non-fatal.
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);
}
}
}
当system进程crash的信息:
开头*** FATAL EXCEPTION IN SYSTEM PROCESS [线程名];
接着输出发生crash时的调用栈信息;
当app进程crash时的信息:
开头FATAL EXCEPTION: [线程名];
紧接着 Process: [进程名], PID: [进程id];
最后输出发生crash时的调用栈信息,
app crash 保存在crash_log中。
当输出完crash信息到logcat里面,这只是crash流程的刚开始阶段,接下来弹出crash对话框,经过binder调用最终交给ActivityManagerService(简称AMS)中相应的方法去处理,故接下来调用的是AMS.handleApplicationCrash()。
3. handleApplicationCrash
[–>ActivityManagerService.java]
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
//获取Processrecord对象 【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();对于代码mProcessNames.getMap()返回的是mMap,而mMap= new ArrayMap<String, SparseArray>();
知识延伸: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。
有了进程记录对象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,...);
.....
//错误信息封装,并写入手机存储
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
//【5】
mAppErrors.crashApplication(r, crashInfo);
}
其中addErrorToDropBox是将crash的信息输出到目录/data/system/dropbox。例如system_server的dropbox文件名为[email protected] (xxx代表的是时间戳)
5. crashApplication&crashApplicationInner
[–>AppErrors.java]
void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
crashApplicationInner(r, crashInfo, callingPid, callingUid);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
int callingPid, int callingUid) {
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();
TaskRecord task;
synchronized (mService) {
......
AppErrorDialog.Data data = new AppErrorDialog.Data();
data.result = result;
data.proc = r;
// If we can't identify the process or it's already exceeded its crash quota,
// quit right away without showing a crash dialog. 【6】
if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
return;
}
final Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
task = data.task;
msg.obj = data;
//发送显示crash dialog message
mService.mUiHandler.sendMessage(msg);
}
//根据弹出框用户选择结果做不同的操作
int res = result.get();
....
}
该方法主要做的3件事:
- 调用makeAppCrashingLocked,继续处理crash流程;
- 发送消息SHOW_ERROR_UI_MSG,弹出提示crash的对话框,等待用户选择;
- 根据用户操作,执行不同的操作,kill Process或者发送错误报告等。
6. makeAppCrashingLocked
[–>ActivityManagerService.java]
private boolean makeAppCrashingLocked(ProcessRecord app, String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
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,data);
}
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
mService.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.3】
}
}
7.3 skipCurrentReceiverLocked
[-> BroadcastQueue.java]
public void skipCurrentReceiverLocked(ProcessRecord app) {
BroadcastRecord r = null;
//检测是否含有有序广播
if (mOrderedBroadcasts.size() > 0) {
BroadcastRecord br = mOrderedBroadcasts.get(0);
if (br.curApp == app) {
r = br;
}
}
//检测当前pendingBroadCast进程是否是当前进程
if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"[" + mQueueName + "] skip & discard pending app " + r);
r = mPendingBroadcast;
}
if (r != null) {
//【7.4】
skipReceiverLocked(r);
}
}
7.4 skipReceiverLocked
[-> BroadcastQueue.java]
private void skipReceiverLocked(BroadcastRecord r) {
logBroadcastReceiverDiscardLocked(r);
//取消广播接收
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,停止进程里所有的Activity
8.1. AR.stopFreezingScreenLocked
[-> ActivityRecord.java]
public void stopFreezingScreenLocked(boolean force) {
if (force || frozenBeforeDestroy) {
frozenBeforeDestroy = false;
mWindowContainerController.stopFreezingScreen(force);
}
}
8.2. AWCC.stopFreezingScreenLocked
[-> AppWindowContainerController.java]
public void stopFreezingScreen(boolean force) {
synchronized(mWindowMap) {
if (mContainer == null) {
return;
}
mContainer.stopFreezingScreen(true, force);
}
}
其中mContainer是appWindowToken对象,故调用AppWindowToken的stopFreezingScreen方法,并且持WMS锁。
8.3 AWT.stopFreezingScreen
[-> AppWindowToken.java]
void stopFreezingScreen(boolean unfreezeSurfaceNow, boolean force) {
if (!mFreezingScreen) {
return;
}
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + this + " force=" + force);
final int count = mChildren.size();
boolean unfrozeWindows = false;
for (int i = 0; i < count; i++) {
final WindowState w = mChildren.get(i);
unfrozeWindows |= w.onStopFreezingScreen();
}
if (force || unfrozeWindows) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
mFreezingScreen = false;
mService.unregisterAppFreezeListener(this);
mService.mAppsFreezingScreen--;
mService.mLastFinishedFreezeSource = this;
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
mService.mWindowPlacerLocked.performSurfacePlacement();
}
mService.stopFreezingDisplayLocked();
}
}
unfreezeSurfaceNow由上一方法传入为true,所以最后调用WMS的stopFreezingDisplayLocked.
8.4 WMS.stopFreezingDisplayLocked
[-> WindowManagerService.java]
private void stopFreezingDisplayLocked() {
if (!mDisplayFrozen) {
return; //显示没有冻结,则直接返回
}
//往往跟屏幕旋转相关
...
final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
final int displayId = mFrozenDisplayId;
mFrozenDisplayId = INVALID_DISPLAY;
mDisplayFrozen = false;
//使能输入事件分发功能
mInputMonitor.thawInputDispatchingLw();
//从上次冻屏到现在的总时长
mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);
//移除冻屏的超时消息
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
boolean updateRotation = false;
ScreenRotationAnimation screenRotationAnimation =
mAnimator.getScreenRotationAnimationLocked(displayId);
//加载屏幕旋转的动画
if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
&& screenRotationAnimation.hasScreenshot()) {
if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
// TODO(multidisplay): rotation on main screen only.
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
mExitAnimId = mEnterAnimId = 0;
}
if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
mTransaction.apply();
scheduleAnimationLocked();
} else {
screenRotationAnimation.kill();
mAnimator.setScreenRotationAnimationLocked(displayId, null);
updateRotation = true;
}
} else {
if (screenRotationAnimation != null) {
screenRotationAnimation.kill();
mAnimator.setScreenRotationAnimationLocked(displayId, null);
}
updateRotation = true;
}
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.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
}
该方法主要功能:
- 处理屏幕旋转相关逻辑;
- 使能输入事件分发功能;
- 移除冻屏的超时消息;
- 屏幕旋转动画的相关操作;
- display冻结时,执行gc操作;
- 更新当前的屏幕方向;
- 向mH发送configuraion改变的消息。
9.AMS.handleAppCrashLocked
[-> ActivityManagerService.java]
boolean handleAppCrashLocked(ProcessRecord app, String reason,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
final long now = SystemClock.uptimeMillis();
final boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
final boolean procIsBoundForeground =
(app.curProcState == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
Long crashTime;
Long crashTimePersistent;
boolean tryAgain = false;
if (!app.isolated) {
crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
crashTimePersistent = mProcessCrashTimesPersistent.get(app.info.processName, app.uid);
} else {
crashTime = null;
}
//统计当前在proc中运行的任何服务的Crash计数。
for (int i = app.services.size() - 1; i >= 0; i--) {
////需要将应用程序中运行的任何服务放回到挂起列表中
ServiceRecord sr = app.services.valueAt(i);
//如果服务刚刚重启,则重置崩溃计数,否则增加它。
if (now > sr.restartTime + ProcessList.MIN_CRASH_INTERVAL) {
sr.crashCount = 1;
} else {
sr.crashCount++;
}
//允许重新启动正在Crash的已启动或绑定的前台服务。包括壁纸
if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
&& (sr.isForeground || procIsBoundForeground)) {
tryAgain = true;
}
}
//当同一个进程,连续两次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】
mService.removeProcessLocked(app, false, tryAgain, "crash");
//恢复最顶部的Activity【9.3】
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
if (!showBackground) {
return false;
}
}
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
} else {
//此处reason="force-crash"【9.4】
final TaskRecord affectedTask =
mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app, reason);
if (data != null) {
data.task = affectedTask;
}
if (data != null && crashTimePersistent != null
&& now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
data.repeating = true;
}
}
if (data != null && tryAgain) {
data.isRestartableForService = true;
}
//当桌面应用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()) {
//清空偏爱应用
try {
ActivityThread.getPackageManager()
.clearPackagePreferredActivities(r.packageName);
} catch (RemoteException c) {
// pm is in same process, this will never happen.
}
}
}
}
if (!app.isolated) {
//无法跟踪隔离进程的崩溃时间,因为它们没有持久标识
mProcessCrashTimes.put(app.info.processName, app.uid, now);
mProcessCrashTimesPersistent.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.kill 杀进程。
- [9.3] mStackSupervisor.resumeTopActivitiesLocked();
对于persistent进程,则只执行mStackSupervisor.resumeTopActivitiesLocked();
否则执行 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,记录着所有先前的后台activities。遍历所有activities,找到位于该ProcessRecord的所有ActivityRecord,并结束该Acitivity。
9.2 AMS.removeProcessLocked
[-> ActivityManagerService.java]
private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) {
...
if (app.pid > 0 && app.pid != MY_PID) {
.....
// 杀进程
app.kill(reason, true);
//移除进程并清空该进程相关联的activity/service等组件
handleAppDiedLocked(app, willRestart, allowRestart);
if (willRestart) {
//此处willRestart=false,不进入该分支
removeLruProcessLocked(app);
addAppLocked(app.info, false, null /* ABI override */);
}
} else {
mRemovedProcesses.add(app);
}
return needRestart;
}
这里主要是进程kill以及相关的Activity或者Service等组件清理工作。
9.3 ASS.resumeTopActivitiesLocked
[-> ActivityStackSupervisor.java]
boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target, Bundle targetOptions) {
if (targetStack == null) {
targetStack = mFocusedStack;
}
boolean result = false;
if (isFrontStack(targetStack)) {
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;
}
被直接kill的进程不需要弹框,则直接启动下一个activity的ActivityStack,这块流程在Activity启动流程中详细介绍。
执行到这,我们还回过来看 5.crashApplication中,处理完makeAppCrashingLocked,则会再发送消息SHOW_ERROR_UI_MSG,弹出提示crash的对话框,接下来再看看该过程。
10. UiHandler
通过mUiHandler发送message,且消息的msg.waht=ActivityManagerService.SHOW_ERROR_UI_MSG;,接下来进入来看看handleMessage的处理过程。
[-> ActivityManagerService.java]
final class UiHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ERROR_UI_MSG: {
mAppErrors.handleShowAppErrorUi(msg);
ensureBootCompleted();
} break;
...
}
}
10.1 handleShowAppErrorUi
[AppErrors.java]
void handleShowAppErrorUi(Message msg) {
AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
AppErrorDialog dialogToShow = null;
final String packageName;
final int userId;
synchronized (mService) {
final ProcessRecord proc = data.proc;
final AppErrorResult res = data.result;
if (proc == null) {
Slog.e(TAG, "handleShowAppErrorUi: proc is null");
return;
}
packageName = proc.info.packageName;
userId = proc.userId;
if (proc.crashDialog != null) {
Slog.e(TAG, "App already has crash dialog: " + proc);
if (res != null) {
res.set(AppErrorDialog.ALREADY_SHOWING);
}
return;
}
boolean isBackground = (UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID
&& proc.pid != MY_PID);
for (int profileId : mService.mUserController.getCurrentProfileIds()) {
isBackground &= (userId != profileId);
}
if (isBackground && !showBackground) {
Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
if (res != null) {
res.set(AppErrorDialog.BACKGROUND_USER);
}
return;
}
final boolean showFirstCrash = Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0;
final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
0,
mService.mUserController.getCurrentUserId()) != 0;
final boolean crashSilenced = mAppsNotReportingCrashes != null &&
mAppsNotReportingCrashes.contains(proc.info.packageName);
if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced
&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
//创建Crash Dialog
proc.crashDialog = dialogToShow = new AppErrorDialog(mContext, mService, data);
} else {
// The device is asleep, so just pretend that the user
// saw a crash dialog and hit "force quit".
if (res != null) {
res.set(AppErrorDialog.CANT_SHOW);
}
}
}
// If we've created a crash dialog, show it without the lock held
if (dialogToShow != null) {
Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId);
dialogToShow.show();//显示
}
}
在发生crash时,默认系统会弹出提示crash的对话框,并阻塞等待用户选择是“退出”或 “退出并报告”,当用户不做任何选择时5min超时后,默认选择“退出”,当手机休眠时也默认选择“退出”。到这里也并没有真正结束,在小节2.uncaughtException中在finnally语句块还有一个杀进程的动作。
11. killProcess
Process.killProcess(Process.myPid());
System.exit(10);
通过finnally语句块保证能执行并彻底杀掉Crash进程。当Crash进程被杀后,并没有完全结束,还有Binder死亡通知的流程还没有处理完成,关于这块在进程管理中详细讲。
12. 小结
当进程抛出未捕获异常时,则系统会处理该异常并进入crash处理流程。
app_crash
其中最为核心的工作图中红色部分AMS.handleAppCrashLocked的主要功能:
当同一进程1分钟之内连续两次crash,则执行的情况下:
(1) 对于非persistent进程:
- ASS.handleAppCrashLocked, 直接结束该应用所有activity
- AMS.removeProcessLocked,杀死该进程以及同一个进程组下的所有进
- ASS.resumeTopActivitiesLocked,恢复栈顶第一个非finishing状态的activity
(2)对于persistent进程:
-
ASS.resumeTopActivitiesLocked,恢复栈顶第一个非finishing状态的activity
否则,当进程没连续频繁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
三、 总结
本文主要以源码的视角,详细介绍了到应用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的流程。
通过mUiHandler发送消息SHOW_ERROR_UI_MSG,弹出crash对话框;
到此,system_server进程执行完成。