接go语言网站开发seo流量软件
1. RuntimeInit.commonInit()
- 上层应用都是由Zygote fork孵化出来的,分为system_server进程和普通应用进程
- 进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时候都会交给异常处理器
- RuntimeInit.java的
commonInit
方法设置UncaughtHandler
//frameworks/base/core/java/com/android/internal/os/RuntimeInit.javaprotected 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.javapublic static void setUncaughtExceptionPreHandler(UncaughtExceptionHandler eh) {uncaughtExceptionPreHandler = eh;}
// 注意 一个setUncaughtExceptionPreHandler, 一个setDefaultUncaughtExceptionHandlerpublic 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;}
1.1LoggingHandler
UncaughtExceptionHandler只定义了一个接口方法 public void uncaughtException(java.lang.Thread t, java.lang.Throwable e)
private static class LoggingHandler implements Thread.UncaughtExceptionHandler {public volatile boolean mTriggered = false;@Overridepublic 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 开头信息
- 当 system 进程crash提示 *** FATAL EXCEPTION IN SYSTEM PROCESS: [线程名]
- 当 app 进程crash 提示三个内容:
- FATAL EXCEPTION: [线程名]
- Process: [进程名], PID: [pid]
- 对于processName 为null,只会提示PID。
1.2 setUncaughtExceptionPreHandler
@SystemApi(client = MODULE_LIBRARIES)public static void setUncaughtExceptionPreHandler(@Nullable Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);}
1.3 KillApplicationHandler
//frameworks/base/core/java/com/android/internal/os/RuntimeInit.javaprivate static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {private final LoggingHandler mLoggingHandler;public KillApplicationHandler(LoggingHandler loggingHandler) {this.mLoggingHandler = Objects.requireNonNull(loggingHandler);}@Overridepublic void uncaughtException(Thread t, Throwable e) {...}...}
KillApplicationHandler 类,以及构造中传入的LoggingHandler,都是实现UncaughtExceptionHandler 接口
2 KillApplicationHandler.uncaughtException()
当线程因为未捕获的异常停止时,java虚拟机会调用uncaughtException()函数,即调用KillApplicationHandler 中的 uncaughtException() 函数,下面好好看下uncaughtException() 函数
//frameworks/base/core/java/com/android/internal/os/RuntimeInit.javapublic void uncaughtException(Thread t, Throwable e) {try {//调用LoggingHandler.uncaughtException(),不会反复调用ensureLogging(t, e);//全局变量,用以控制重复进入crash流程,第一次进入后会将该变量置trueif (mCrashing) return;mCrashing = true;//尝试去停止profiling,因为后面需要kill 进程,内存buffer会丢失,//所以尝试停止,来 flush 内存bufferif (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,共两个参数
- 第一个参数为进程对象
- 第二个参数为ParcelableCrashInfo(父类为 CrashInfo , 实现 Parcelable接口)
- CrashInfo 类主要是保存 crash 信息:文件名、类名、方法名、对应行号、异常信息等
3 AMS.handleApplicationCrash()
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javapublic 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.javavoid 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.javavoid 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);}}}
- 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@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.1errState.setCrashingReport(generateProcessError(app,ActivityManager.ProcessErrorStateInfo.CRASHED,null, shortMsg, longMsg, stackTrace));//5.2errState.startAppProblemLSP();app.getWindowProcessController().stopFreezingActivities();synchronized (mBadProcessLock) {//5.3return handleAppCrashLSPB(app, "force-crash" /*reason*/, shortMsg, longMsg,stackTrace, data);}}}
5.1 generateProcessError()
//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;}
5.2 startAppProblemLSP()
//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);}
- 当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.javapublic static ComponentName getErrorReportReceiver(Context context,String packageName, int appFlags) {//首先global表中需要enable send_action_app_error属性,否则return nullint 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 packageString 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.javastatic ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,String receiverPackage) {//候选的包名为null 或为空,返回nullif (receiverPackage == null || receiverPackage.length() == 0) {return null;}// 如果与crash的应用包名相同if (receiverPackage.equals(errorPackage)) {return null;}//action 为android.intent.action.APP_ERRORIntent 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 信息:
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.javapublic 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 appr = 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.javapublic void stopFreezingActivities() {synchronized (mAtm.mGlobalLock) {int i = mActivities.size();while (i > 0) {i--;mActivities.get(i).stopFreezingScreenLocked(true);}}}//其中的 mActivities 为 ArrayList<ActivityRecord>,停止进程里所有activity。
//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.javavoid 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@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;}
6. SHOW_ERROR_UI_MSG 消息
当makeAppCrashingLocked() 返回true 时,会通过 AMS.mUiHandler发送 SHOW_ERROR_UI_MSG 消息:
//frameworks/base/services/core/java/com/android/server/am/AMS.javapublic 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.javapublic void uncaughtException(Thread t, Throwable e) {try {...} catch (Throwable t2) {...} finally {//确保当前进程彻底杀掉Process.killProcess(Process.myPid());System.exit(10);}}
8. 避开uncaught exception
java 端提供了一个接口:
//libcore/ojluni/src/main/java/java/lang/Thread.javaprivate 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 threadThread thread = new Thread(new Runnable() {@Overridepublic void run() {String str = null;System.out.println(str.length());}});thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {@Overridepublic void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {}});thread.start();return;}
上面是个简单的示例,只要通过 setUncaughtExceptionHandler() 设置一个新的UncaughtExceptionHandler, 就可以避开上述问题。
9 总结
9.1 调用栈
- KillApplicationHandler.uncaughtException()
- stopProfiling
- AMS.handleApplicationCrash()
- handleApplicationCrashInner
- AppErrors.crashApplication()
- makeAppCrashingLocked
- generateProcessError()
- startAppProblemLSP()
- getErrorReportReceiver
- AMS.skipCurrentReceiverLocked()
- stopFreezingActivities()
- handleAppCrashLocked
9.2 handleAppCrashLocked
当同一进程 1 分钟之内连续两次 crash,则执行:
- mService.mAtmInternal.onHandleAppCrash()
- 对于非 persistent 进程:
- ·mService.mProcessList.removeProcessLocked()
- mService.mAtmInternal.resumeTopActivities()
- 对于 persistent 进程:
- mService.mAtmInternal.resumeTopActivities()
- 没有在1分钟频繁 crash,则执行
- mService.mAtmInternal.finishTopCrashedActivities()