commonInit
方法设置UncaughtHandler//frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static final void commonInit() {
...
LoggingHandler loggingHandler = new LoggingHandler();
RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
...
}
········
//libcore/dalvik/src/main/java/dalvik/system/RuntimeHooks.java
@SystemApi(client = MODULE_LIBRARIES)
public static void setUncaughtExceptionPreHandler(
@Nullable Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
}
···········
//libcore/ojluni/annotations/hiddenapi/java/lang/Thread.java
public static void setUncaughtExceptionPreHandler(UncaughtExceptionHandler eh) {
uncaughtExceptionPreHandler = eh;
}
// 注意 一个setUncaughtExceptionPreHandler, 一个setDefaultUncaughtExceptionHandler
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
// Android-removed: SecurityManager stubbed out on Android.
/*
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(
new RuntimePermission("setDefaultUncaughtExceptionHandler")
);
}
*/
defaultUncaughtExceptionHandler = eh;
}
UncaughtExceptionHandler只定义了一个接口方法 public void uncaughtException(java.lang.Thread t, java.lang.Throwable e)
private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
public volatile boolean mTriggered = false;
@Override
public void uncaughtException(Thread t, Throwable e) {
mTriggered = true;
// Don't re-enter if KillApplicationHandler has already run
//已经在crash 流程中,则已经在处理KillApplicationHandler则不再重复进入
if (mCrashing) return;
// mApplicationObject is null for non-zygote java programs (e.g. "am")
// There are also apps running with the system UID. We don't want the
// first clause in either of these two cases, only for system_server.
if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
// sysyem_server进程
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
logUncaught(t.getName(), ActivityThread.currentProcessName(), Process.myPid(), e);
}
}
}
........................
public static void logUncaught(String threadName, String processName, int pid, Throwable e) {
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.
//这个就是日志经常看到的 TAG是AndroidRuntime,打印
message.append("FATAL EXCEPTION: ").append(threadName).append("\n");
if (processName != null) {
message.append("Process: ").append(processName).append(", ");
}
message.append("PID: ").append(pid);
Clog_e(TAG, message.toString(), e);
}
当线程因为未捕获的异常停止时,Java 虚拟机会调用 uncaughtException() 函数。
可以看出,uncaughtException() 是在crash 最开始调用的,用以输出crash 开头信息
@SystemApi(client = MODULE_LIBRARIES)
public static void setUncaughtExceptionPreHandler(
@Nullable Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
}
//frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
private final LoggingHandler mLoggingHandler;
public KillApplicationHandler(LoggingHandler loggingHandler) {
this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
...
}
...
}
KillApplicationHandler 类,以及构造中传入的LoggingHandler,都是实现UncaughtExceptionHandler 接口
当线程因为未捕获的异常停止时,java虚拟机会调用uncaughtException()函数,即调用KillApplicationHandler 中的 uncaughtException() 函数,下面好好看下uncaughtException() 函数
//frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
public void uncaughtException(Thread t, Throwable e) {
try {
//调用LoggingHandler.uncaughtException(),不会反复调用
ensureLogging(t, e);
//全局变量,用以控制重复进入crash流程,第一次进入后会将该变量置true
if (mCrashing) return;
mCrashing = true;
//尝试去停止profiling,因为后面需要kill 进程,内存buffer会丢失,
//所以尝试停止,来 flush 内存buffer
if (ActivityThread.currentActivityThread() != null) {
ActivityThread.currentActivityThread().stopProfiling();
}
//弹出crash对话框,等待处理完成
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(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 {
//确保当前进程彻底杀掉
Process.killProcess(Process.myPid());
System.exit(10);
}
}
通过 AMS 调用 handleApplicationCrash() 函数进行 crash report,共两个参数
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void handleApplicationCrash(IBinder app,
ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
ProcessRecord r = findAppProcess(app, "Crash");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
handleApplicationCrashInner("crash", r, processName, crashInfo);
}
该函数主要两个操作:
对于进程名,
//frameworks/base/services/core/java/com/android/server/am/AMS.java
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
EventLogTags.writeAmCrash(Binder.getCallingPid(),
UserHandle.getUserId(Binder.getCallingUid()), processName,
r == null ? -1 : r.info.flags,
crashInfo.exceptionClassName,
crashInfo.exceptionMessage,
crashInfo.throwFileName,
crashInfo.throwLineNumber);
FrameworkStatsLog.write(FrameworkStatsLog.APP_CRASH_OCCURRED,
Binder.getCallingUid(),
eventType,
processName,
Binder.getCallingPid(),
(r != null && r.info != null) ? r.info.packageName : "",
(r != null && r.info != null) ? (r.info.isInstantApp()
? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
: FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__FALSE)
: FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__UNAVAILABLE,
r != null ? (r.isInterestingToUserLocked()
? FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__FOREGROUND
: FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__BACKGROUND)
: FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN,
processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
: (r != null) ? r.getProcessClassEnum()
: ServerProtoEnums.ERROR_SOURCE_UNKNOWN
);
final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
: r.getWindowProcessController().computeRelaunchReason();
final String relaunchReasonString = relaunchReasonToString(relaunchReason);
if (crashInfo.crashTag == null) {
crashInfo.crashTag = relaunchReasonString;
} else {
crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
}
addErrorToDropBox(
eventType, r, processName, null, null, null, null, null, null, crashInfo);
mAppErrors.crashApplication(r, crashInfo);
}
函数比较长,主要做了下面几件事情
写event log 类似:
12-01 16:45:29.663198 1260 3220 I am_crash: [21597,0,com.qualcomm.qti.PresenceApp,550026821,java.lang.NoSuchMethodException,com.qualcomm.qti.PresenceApp.SubsriptionTab.
addErrorToDropBox() 将crash 的信息输出到 /data/system/dropbox/ 下,例如system_server 的dropbox 文件名为 [email protected] (xxx 代表时间戳);
crashApplication() 继续处理 crash 流程,发出 SHOW_ERROR_UI_MSG,弹出 crash 对话框;
//frameworks/base/services/core/java/com/android/server/am/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);
}
}
//-------------------------------------------------------------------------------------------
private void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
int callingPid, int callingUid) {
.........................................................
AppErrorResult result = new AppErrorResult();
int taskId;
synchronized (mService) {
/**
* If crash is handled by instance of {@link android.app.IActivityController},
* finish now and don't show the app error dialog.
*/
if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
timeMillis, callingPid, callingUid)) {
return;
}
// If we can't identify the process or it's already exceeded its crash quota,
// quit right away without showing a crash dialog.
if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
return;
}
final Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
taskId = data.taskId;
msg.obj = data;
mService.mUiHandler.sendMessage(msg);
}
int res = result.get();
Intent appErrorIntent = null;
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
res = AppErrorDialog.FORCE_QUIT;
}
switch (res) {
case AppErrorDialog.MUTE:
synchronized (mBadProcessLock) {
stopReportingCrashesLBp(r);
}
break;
case AppErrorDialog.RESTART:
synchronized (mService) {
mService.mProcessList.removeProcessLocked(r, false, true,
ApplicationExitInfo.REASON_CRASH, "crash");
}
if (taskId != INVALID_TASK_ID) {
try {
mService.startActivityFromRecents(taskId,
ActivityOptions.makeBasic().toBundle());
} catch (IllegalArgumentException e) {
// Hmm...that didn't work. Task should either be in recents or associated
// with a stack.
Slog.e(TAG, "Could not restart taskId=" + taskId, e);
}
}
break;
case AppErrorDialog.FORCE_QUIT:
final long orig = Binder.clearCallingIdentity();
try {
// Kill it with fire!
mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
if (!r.isPersistent()) {
synchronized (mService) {
mService.mProcessList.removeProcessLocked(r, false, false,
ApplicationExitInfo.REASON_CRASH, "crash");
}
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
}
} finally {
Binder.restoreCallingIdentity(orig);
}
break;
case AppErrorDialog.APP_INFO:
appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
break;
case AppErrorDialog.FORCE_QUIT_AND_REPORT:
synchronized (mProcLock) {
appErrorIntent = createAppErrorIntentLOSP(r, timeMillis, crashInfo);
}
break;
}
if (appErrorIntent != null) {
try {
mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "bug report receiver dissappeared", e);
}
}
}
//frameworks/base/services/core/java/com/android/server/am/AppErrors.java
@GuardedBy("mService")
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
synchronized (mProcLock) {
final ProcessErrorStateRecord errState = app.mErrorState;
errState.setCrashing(true);
//封装crash信息到crashingReport对象 generateProcessError 5.1
errState.setCrashingReport(generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.CRASHED,
null, shortMsg, longMsg, stackTrace));
//5.2
errState.startAppProblemLSP();
app.getWindowProcessController().stopFreezingActivities();
synchronized (mBadProcessLock) {
//5.3
return handleAppCrashLSPB(app, "force-crash" /*reason*/, shortMsg, longMsg,
stackTrace, data);
}
}
}
//frameworks/base/services/core/java/com/android/server/am/AppErrors.java
//通过该函数创建 ActivityManager.ProcessErrorStateInfo 对象,并保存在 app.crashingReport 对象中
ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
report.condition = condition;
report.processName = app.processName;
report.pid = app.pid;
report.uid = app.info.uid;
report.tag = activity;
report.shortMsg = shortMsg;
report.longMsg = longMsg;
report.stackTrace = stackTrace;
return report;
}
//frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
@GuardedBy({"mService", "mProcLock"})
void startAppProblemLSP() {
// If this app is not running under the current user, then we can't give it a report button
// because that would require launching the report UI under a different user.
mErrorReportReceiver = null;
for (int userId : mService.mUserController.getCurrentProfileIds()) {
if (mApp.userId == userId) {
mErrorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mService.mContext, mApp.info.packageName, mApp.info.flags);
}
}
mService.skipCurrentReceiverLocked(mApp);
}
//frameworks/base/core/java/android/app/ApplicationErrorReport.java
public static ComponentName getErrorReportReceiver(Context context,
String packageName, int appFlags) {
//首先global表中需要enable send_action_app_error属性,否则return null
int enabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SEND_ACTION_APP_ERROR, 0);
if (enabled == 0) {
return null;
}
PackageManager pm = context.getPackageManager();
// look for receiver in the installer package
String candidate = null;
ComponentName result = null;
try {
//获取该crash应用的包名
candidate = pm.getInstallerPackageName(packageName);
} catch (IllegalArgumentException e) {
// the package could already removed
}
//第一次获取,使用的是crash应用的包名
if (candidate != null) {
result = getErrorReportReceiver(pm, packageName, candidate);
if (result != null) {
return result;
}
}
//第二次获取,使用prop ro.error.receiver.system.apps指定的包名
if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
result = getErrorReportReceiver(pm, packageName, candidate);
if (result != null) {
return result;
}
}
//第三次获取,使用prop ro.error.receiver.default指定的包名
candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
return getErrorReportReceiver(pm, packageName, candidate);
}
上面最终会调用 getErrorReportReceiver() 的另一个函数,最后的参数为最终的候选包名:
//frameworks/base/core/java/android/app/ApplicationErrorReport.java
static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
String receiverPackage) {
//候选的包名为null 或为空,返回null
if (receiverPackage == null || receiverPackage.length() == 0) {
return null;
}
// 如果与crash的应用包名相同
if (receiverPackage.equals(errorPackage)) {
return null;
}
//action 为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;
}
return new ComponentName(receiverPackage, info.activityInfo.name);
}
需要注意的是寻找这个report receiver 是为了在此处makeAppCrashingLocked() 函数返回,crash 对话框弹出之后,根据用户的选择做进一步的处理,包括了通过该 receiver 发从 report 信息:
void skipCurrentReceiverLocked(ProcessRecord app) {
for (BroadcastQueue queue : mBroadcastQueues) {
queue.skipCurrentReceiverLocked(app);
}
}
//frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
public void skipCurrentReceiverLocked(ProcessRecord app) {
BroadcastRecord r = null;
final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
if (curActive != null && curActive.curApp == app) {
// confirmed: the current active broadcast is to the given app
r = curActive;
}
// If the current active broadcast isn't this BUT we're waiting for
// mPendingBroadcast to spin up the target app, that's what we use.
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) {
skipReceiverLocked(r);
}
}
private void skipReceiverLocked(BroadcastRecord r) {
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
}
主要是结束 app进程的广播
//frameworks/base/services/core/java/com/android/server/am/WindowProcessController.java
public void stopFreezingActivities() {
synchronized (mAtm.mGlobalLock) {
int i = mActivities.size();
while (i > 0) {
i--;
mActivities.get(i).stopFreezingScreenLocked(true);
}
}
}
//其中的 mActivities 为 ArrayList,停止进程里所有activity。
//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
void stopFreezingScreenLocked(boolean force) {
if (force || frozenBeforeDestroy) {
frozenBeforeDestroy = false;
if (getParent() == null) {
return;
}
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Clear freezing of %s: visible=%b freezing=%b", appToken,
isVisible(), isFreezingScreen());
stopFreezingScreen(true, force);
}
}
最终调用的是 WSM.stopFreezingDisplayLocked() 函数,详细可以查看源码,大致流程:
这个crash 后续操作流程的核心处理函数:
//frameworks/base/services/core/java/com/android/server/am/AppErrors.java
@GuardedBy({"mService", "mProcLock", "mBadProcessLock"})
private boolean handleAppCrashLSPB(ProcessRecord app, String reason,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
final long now = SystemClock.uptimeMillis();
final boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0,
mService.mUserController.getCurrentUserId()) != 0;
Long crashTime;
Long crashTimePersistent;
final String processName = app.processName;
final int uid = app.uid;
final int userId = app.userId;
final boolean isolated = app.isolated;
final boolean persistent = app.isPersistent();
final WindowProcessController proc = app.getWindowProcessController();
final ProcessErrorStateRecord errState = app.mErrorState;
if (!app.isolated) {
crashTime = mProcessCrashTimes.get(processName, uid);
crashTimePersistent = mProcessCrashTimesPersistent.get(processName, uid);
} else {
crashTime = crashTimePersistent = null;
}
// Bump up the crash count of any services currently running in the proc.
boolean tryAgain = app.mServices.incServiceCrashCountLocked(now);
final boolean quickCrash = crashTime != null
&& now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
if (quickCrash || isProcOverCrashLimitLBp(app, now)) {
// The process either crashed again very quickly or has been crashing periodically in
// the last few hours. If it was a bound foreground service, let's try to restart again
// in a while, otherwise the process loses!
Slog.w(TAG, "Process " + processName + " has crashed too many times, killing!"
+ " Reason: " + (quickCrash ? "crashed quickly" : "over process crash limit"));
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
userId, processName, uid);
mService.mAtmInternal.onHandleAppCrash(proc);
if (!persistent) {
// We don't want to start this process again until the user
// explicitly does so... but for persistent process, we really
// need to keep it running. If a persistent process is actually
// repeatedly crashing, then badness for everyone.
EventLog.writeEvent(EventLogTags.AM_PROC_BAD, userId, uid,
processName);
if (!isolated) {
// XXX We don't have a way to mark isolated processes
// as bad, since they don't have a persistent identity.
markBadProcess(processName, app.uid,
new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
mProcessCrashTimes.remove(processName, app.uid);
mProcessCrashCounts.remove(processName, app.uid);
}
errState.setBad(true);
app.setRemoved(true);
final AppStandbyInternal appStandbyInternal =
LocalServices.getService(AppStandbyInternal.class);
if (appStandbyInternal != null) {
appStandbyInternal.restrictApp(
// Sometimes the processName is the same as the package name, so use
// that if we don't have the ApplicationInfo object.
// AppStandbyController will just return if it can't find the app.
app.info != null ? app.info.packageName : processName,
userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
}
// Don't let services in this process be restarted and potentially
// annoy the user repeatedly. Unless it is persistent, since those
// processes run critical code.
mService.mProcessList.removeProcessLocked(app, false, tryAgain,
ApplicationExitInfo.REASON_CRASH, "crash");
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
if (!showBackground) {
return false;
}
}
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
} else {
final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
proc, reason);
if (data != null) {
data.taskId = affectedTaskId;
}
if (data != null && crashTimePersistent != null
&& now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
data.repeating = true;
}
}
if (data != null && tryAgain) {
data.isRestartableForService = true;
}
// If the crashing process is what we consider to be the "home process" and it has been
// replaced by a third-party app, clear the package preferred activities from packages
// with a home activity running in the process to prevent a repeatedly crashing app
// from blocking the user to manually clear the list.
if (proc.isHomeProcess() && proc.hasActivities() && (app.info.flags & FLAG_SYSTEM) == 0) {
proc.clearPackagePreferredForHomeActivities();
}
if (!isolated) {
// XXX Can't keep track of crash times for isolated processes,
// because they don't have a persistent identity.
mProcessCrashTimes.put(processName, uid, now);
mProcessCrashTimesPersistent.put(processName, uid, now);
updateProcessCrashCountLBp(processName, uid, now);
}
if (errState.getCrashHandler() != null) {
mService.mHandler.post(errState.getCrashHandler());
}
return true;
}
当makeAppCrashingLocked() 返回true 时,会通过 AMS.mUiHandler发送 SHOW_ERROR_UI_MSG 消息:
//frameworks/base/services/core/java/com/android/server/am/AMS.java
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ERROR_UI_MSG: {
mAppErrors.handleShowAppErrorUi(msg);
ensureBootCompleted();
} break;
最终是调用 mAppErrors.handleShowAppErrorUi(),代码逻辑不是很复杂,这里暂时不做剖析。
正常情况在发生 crash 时,默认系统会弹出提示 crash 的对话框,并阻塞等待用户的选择。
crash 的处理流程大致剖析完成,回到第 2 节 KillApplicationHandler.uncaughtException() 最后的finally:
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
public void uncaughtException(Thread t, Throwable e) {
try {
...
} catch (Throwable t2) {
...
} finally {
//确保当前进程彻底杀掉
Process.killProcess(Process.myPid());
System.exit(10);
}
}
java 端提供了一个接口:
//libcore/ojluni/src/main/java/java/lang/Thread.java
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
在app 出现 crash 的时候,需要确定当线程中是否设置了 uncaughtExceptionHandler,那么原来的 defaultUncaughtExceptionHandler 将不会被调用,即如果设置了 uncaughtExceptionHandler,最终调用的是 uncaughtExceptionHandler.uncaughtException()。
利用这种方式可以避开 uncaught exception 而引起的 app 被kill。例如:
if (true) {
//create thread
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String str = null;
System.out.println(str.length());
}
});
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
}
});
thread.start();
return;
}
上面是个简单的示例,只要通过 setUncaughtExceptionHandler() 设置一个新的UncaughtExceptionHandler, 就可以避开上述问题。
当同一进程 1 分钟之内连续两次 crash,则执行: