源码基于:Android R
0. 前言
App crash(全称为 Application crash),又分 java crash 和 native crash,又称 java layer exception(JE) 和 native layer exception(NE)。对于 crash 在开发过程中或多或少都会遇到,本文将整理总结 crash 原理,剖析系统是如何捕捉、处理这些 crash。因为篇幅较长,所以会分JE 和 NE 两部分各自剖析。
1. RuntimeInit.commonInit()
在Andriod 系统中,上层应用都是由 Zygote fork 孵化而来,分为system_server 系统进程和普通应用进程。在这些进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时,最终都会交给异常处理器。而这个异常捕获的处理设置就是在 RuntimeInit.commonInit() 函数中完成:
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));
...
}
1.1 Thread.setDefaultUncaughtExceptionHandler()
libcore/ojluni/src/main/java/java/lang/Thread.java
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
defaultUncaughtExceptionHandler = eh;
}
参数的类型为 UncaughtExceptionHandler,这就是未捕获的异常类型。该类型是 interface,后面调用其 uncaughtException() 函数进行处理:
libcore/ojluni/src/main/java/java/lang/Thread.java
public interface UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e);
}
当线程因为未捕获的异常停止时,Java 虚拟机会调用 uncaughtException() 函数。
1.2 KillApplicationHandler 类
从上面的代码看到参数 UncaughtExceptionHandler 为 KillApplicationHandler 对象,这里来看下该类:
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) {
...
}
...
}
该类父类为 Thread.UncaughtExceptionHandler,并实现了 uncaughtException() 函数。
另外,构造中需要传入一个 LoggingHandler 的对象,存放在私有变量 mLoggingHandler 中。
对于核心的处理函数 uncaughtException() 详细见下面第 2 节。
1.3 LoggingHandler 类
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
public volatile boolean mTriggered = false;
@Override
public void uncaughtException(Thread t, Throwable e) {
mTriggered = true;
//已经在crash 流程中,则已经在处理KillApplicationHandler则不再重复进入
if (mCrashing) return;
if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
logUncaught(t.getName(), ActivityThread.currentProcessName(), Process.myPid(), e);
}
}
}
该类中的 uncaughtException() 是在crash 最开始调用的,用以输出crash 开头信息:
- 当 system 进程crash提示 *** FATAL EXCEPTION IN SYSTEM PROCESS: [线程名]
- 当 app 进程crash 提示三个内容:
- FATAL EXCEPTION: [线程名]
- Process: [进程名], PID: [pid]
对于processName 为null,只会提示PID。
2. KillApplicationHandler.uncaughtException()
当线程因为未捕获的异常停止时,Java 虚拟机会调用 uncaughtException() 函数,即调用 KillApplicationHandler 中的 uncaughtException() 函数。本节将在上面第 1.2 节的基础上详细地剖析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) {
...
} finally {
//确保当前进程彻底杀掉
Process.killProcess(Process.myPid());
System.exit(10);
}
}
主要是通过 AMS 调用 handleApplicationCrash() 函数进行 crash report,共两个参数:
- 第一个参数为进程对象
- 第二个参数为ParcelableCrashInfo(父类为 CrashInfo 和 Parcelable)
ParcelableCrashInfo 用以封装 CrashInfo,引入Parcelable。
CrashInfo 类主要是保存 crash 信息:文件名、类名、方法名、对应行号、异常信息等。
详细的 handleApplicationCrash() 函数剖析,请查看下面第 3 节。
3. AMS.handleApplicationCrash()
frameworks/base/services/core/java/com/android/server/am/AMS.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);
}
该函数主要两个操作:
- 确定进程名;
- handleApplicationCrashInner() 函数调用;
对于进程名,
- 当参数 app 为null,表示 system_server 进程;
- 当参数 app不为null,通过findAppProcess() 确认ProcessRecord,进而确认进程名;
3.1 AMS.handleApplicationCrashInner()
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.<init> [],Class.java,2363]
- addErrorToDropBox() 将crash 的信息输出到 /data/system/dropbox/ 下,例如system_server 的dropbox 文件名为 system_server_crash@xxx.txt (xxx 代表时间戳);
- crashApplication() 继续处理 crash 流程,发出 SHOW_ERROR_UI_MSG,弹出 crash 对话框;
4. AppErrors.crashApplication()
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);
}
}
主要是调用私有函数 crashApplicationInner(),因为代码逻辑太多,这里不过多剖析,主要关注如下几点:
- handleAppCrashInActivityController()
如果是 IActivityController 类型该处理的 crash,是不会弹出对话框,通过该函数进入 makeAppCrashingLocked() 流程。
- makeAppCrashingLocked()
如果无法识别进程,或者进程已经超过crash 额度,将不再弹出对话框,而是直接return到上一级。
- 发出 SHOW_ERROR_UI_MSG 消息,弹出crash 对话框,详细看下面第 6 节;
- 等待用户选择,根据不同选择做进一步处理
5. makeAppCrashingLocked()
frameworks/base/services/core/java/com/android/server/am/AppErrors.java
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
app.setCrashing(true);
//封装crash信息到crashingReport对象
app.crashingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
app.startAppProblemLocked();
app.getWindowProcessController().stopFreezingActivities();
return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
data);
}
5.1 generateProcessError()
frameworks/base/services/core/java/com/android/server/am/AppErrors.java
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;
}
通过该函数创建 ActivityManager.ProcessErrorStateInfo 对象,并保存在 app.crashingReport 对象中。
5.2 startAppProblemLocked()
frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
void startAppProblemLocked() {
// 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.
errorReportReceiver = null;
for (int userId : mService.mUserController.getCurrentProfileIds()) {
if (this.userId == userId) {
errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mService.mContext, info.packageName, info.flags);
}
}
mService.skipCurrentReceiverLocked(this);
}
当crash 的app 是当前user 下,会通过 getErrorReportReceiver() 获取应用的report receiver,该receiver 指定的Intent 的action 为:android.intent.action.APP_ERROR。如果没有找到则使用prop ro.error.receiver.default 指定的,否则返回null。
5.2.1 getErrorReportReceiver()
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 信息:
frameworks/base/services/core/java/com/android/server/am/AppErrors.java
Intent createAppErrorIntentLocked(ProcessRecord r,
long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
if (report == null) {
return null;
}
Intent result = new Intent(Intent.ACTION_APP_ERROR);
result.setComponent(r.errorReportReceiver);
result.putExtra(Intent.EXTRA_BUG_REPORT, report);
result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return result;
}
5.2.2 AMS.skipCurrentReceiverLocked()
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进程的广播。
5.3 stopFreezingActivities()
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<ActivityRecord>,停止进程里所有activity。
5.3.1 stopFreezingScreenLocked()
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() 函数,详细可以查看源码,大致流程:
- 将冻屏相关的信息remove 掉;
- 屏幕旋转动画的相关操作;
- display 冻结时,执行GC 操作;
- 更新当前的屏幕方向;
5.4 handleAppCrashLocked()
这个crash 后续操作流程的核心处理函数:
frameworks/base/services/core/java/com/android/server/am/AppErrors.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.getCurProcState() == 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 = crashTimePersistent = null;
}
// 确认crash进程任何services的crash count,确认是否置tryAgain为true
for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
// Any services running in the application need to be placed
// back in the pending list.
ServiceRecord sr = app.getRunningServiceAt(i);
// If the service was restarted a while ago, then reset crash count, else increment it.
if (now > sr.restartTime + ProcessList.MIN_CRASH_INTERVAL) {
sr.crashCount = 1;
} else {
sr.crashCount++;
}
// Allow restarting for started or bound foreground services that are crashing.
// This includes wallpapers.
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) {
// The process crashed again very quickly. If it was a bound foreground service, let's
// try to restart again in a while, otherwise the process loses!
Slog.w(TAG, "Process " + app.info.processName
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.userId, app.info.processName, app.uid);
mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
if (!app.isPersistent()) {
// 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, app.userId, app.uid,
app.info.processName);
if (!app.isolated) {
// XXX We don't have a way to mark isolated processes
// as bad, since they don't have a persistent identity.
markBadProcess(app.info.processName, app.uid,
new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
mProcessCrashTimes.remove(app.processName, app.uid);
}
app.bad = true;
app.removed = true;
// 不再重启非persistent进程,除非用户显示调用,移除所有服务
mService.mProcessList.removeProcessLocked(app, false, tryAgain,
ApplicationExitInfo.REASON_CRASH, "crash");
//恢复最顶端的Activity
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
//注意这里,该变量通常为false,所以,当频繁crash时,返回false,即不会再弹出dialog
if (!showBackground) {
return false;
}
}
//恢复最顶端的Activity
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
} else { //相反,如果crash不频繁,结束crash activity
final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
app.getWindowProcessController(), reason);
if (data != null) {
data.taskId = affectedTaskId;
}
if (data != null && crashTimePersistent != null
&& now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
data.repeating = true;
}
}
if (data != null && tryAgain) {
data.isRestartableForService = true;
}
//当桌面应用crash,并且被第三方app取代,那么需要清空桌面应用的偏爱选项
final WindowProcessController proc = app.getWindowProcessController();
final WindowProcessController homeProc = mService.mAtmInternal.getHomeProcess();
if (proc == homeProc && proc.hasActivities()
&& (((ProcessRecord) homeProc.mOwner).info.flags & FLAG_SYSTEM) == 0) {
proc.clearPackagePreferredForHomeActivities();
}
if (!app.isolated) {
// 无法记录孤立进程的crash时间点,由于他们并没有一个固定身份
mProcessCrashTimes.put(app.info.processName, app.uid, now);
mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now);
}
//当app存在crash的handler,交给其处理
if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
return true;
}
6. SHOW_ERROR_UI_MSG 消息
当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 的对话框,并阻塞等待用户的选择。
7. killProcess()
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 crash 处理流程大致剖析完成,大致流程图总结如下:
其中最核心的是 handleAppCrashLocked():
- 当统一进程 1 分钟之内连续两次 crash,则执行:
- mService.mAtmInternal.onHandleAppCrash()
- 对于非 persistent 进程:
- mService.mProcessList.removeProcessLocked()
- mService.mAtmInternal.resumeTopActivities()
- 对于 persistent 进程:
- mService.mAtmInternal.resumeTopActivities()
- 没有在1分钟频繁 crash,则执行:
- mService.mAtmInternal.finishTopCrashedActivities()
8. 避开uncaught exception
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, 就可以避开上述问题。