稳定性——JE流程
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()
相关文章:

稳定性——JE流程
1. RuntimeInit.commonInit() 上层应用都是由Zygote fork孵化出来的,分为system_server进程和普通应用进程进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时候都会交给异常处理器RuntimeInit.java的commonInit方法设置UncaughtHandler …...
【控制篇 / 分流】(7.4) ❀ 03. 对国内和国际IP网段访问进行分流 ❀ FortiGate 防火墙
【简介】公司有两条宽带用来上网,一条电信,一条IPLS国际专线,由于IPLS仅有2M,且价格昂贵,领导要求,访问国内IP走电信,国际IP走IPLS,那么应该怎么做? 国内IP地址组 我们已…...

01-开始Rust之旅
上一篇:00-Rust前言 1. 下载Rust 官方推荐使用 rustup 下载 Rust,这是一个管理 Rust 版本和相关工具的命令行工具。下载时需要连接互联网。 这边提供了离线安装版本。本人学习的机器环境为: ubuntu x86_64,因此选用第②个工具链&a…...

华南理工大学数字信号处理实验实验一(薛y老师版本)matlab源码
一、实验目的 1、加深对离散信号频谱分析的理解; 2、分析不同加窗长度对信号频谱的影响; 3、理解频率分辨率的概念,并分析其对频谱的 影响; 4、窗长和补零对DFT的影响 实验源码: 第一题: % 定义离散信…...

一篇文章看懂云渲染,云渲染是什么?云渲染如何计费?云渲染怎么选择
云渲染是近年兴起的新行业,很多初学者对它不是很了解,云渲染是什么?为什么要选择云渲染?它是如何计费的又怎么选择?这篇文章我们就带大家了解下吧。 云渲染是什么 云渲染简单来说就是把本地的渲染工作迁移到云端进行的…...

C++进阶--哈希表模拟实现unordered_set和unordered_map
哈希表模拟实现unordered_set和unordered_map 一、定义哈希表的结点结构二、定义哈希表的迭代器三、定义哈希表的结构3.1 begin()和end()的实现3.2 默认成员函数的实现3.2.1 构造函数的实现3.2.2 拷贝构造函数的实现(深拷贝)3.2.3 赋值运算符重载函数的实…...

Elasticsearch各种高级文档操作
本文来记录下Elasticsearch各种文档操作 文章目录 初始化文档数据查询所有文档匹配查询文档关键字精确查询文档多关键字精确查询文档字段匹配查询文档指定查询字段查询文档过滤字段查询文档概述指定想要显示的字段示例指定不想要显示的字段示例 组合查询文档范围查询文档概述使…...

激光无人机打击系统——光束控制和指向系统
激光无人机(UAV)打击系统中的光束控制和指向系统通常包括以下几个关键组件和技术: 激光发射器:这是系统的核心,负责生成高能量的激光束。常用的激光类型包括固体激光器、化学激光器、光纤激光器等,选择取决…...

pycharm import torch
目录 1 安装 2 conda环境配置 3 测试 开始学习Pytorch! 1 安装 我的电脑 Windows 11 Python 3.11 Anaconda3-2023.09-0-Windows-x86_64.exe cuda_11.8.0_522.06_windows.exe pytorch (管理员命令行安装) pycharm-community-2023.3.2.exe 2 c…...

flask 与小程序 购物车删除和编辑库存功能
编辑 : 数量加减 价格汇总 数据清空 mina/pages/cart/index.wxml <!--index.wxml--> <view class"container"><view class"title-box" wx:if"{{ !list.length }}">购物车空空如也~</view>…...

蓝桥杯真题(Python)每日练Day3
题目 题目分析 为了找到满足条件的放置方法,可以带入总盘数为2和3的情景,用递归做法实现。 2. A中存在1 2两个盘,为了实现最少次数放入C且上小下大,先将1放入B,再将2放入C,最后将1放入C即可。同理当A中存在…...

结构体大揭秘:代码中的时尚之选(上)
目录 结构结构的声明结构成员的类型结构体变量的定义和初始化结构体成员的访问结构体传参 结构 结构是一些值的集合,这些值被称为成员变量。之前说过数组是相同类型元素的集合。结构的每个成员可以是不同类型的变量,当然也可以是相同类型的。 我们在生活…...

【unity学习笔记】语音驱动blendershape
1.导入插件 https://assetstore.unity.com/packages/tools/animation/salsa-lipsync-suite-148442 1.选择小人,点击添加组件 分别加入组件: SALSA EmoteR Eyes Queue Processor(必须加此脚本):控制前三个组件的脚本。…...

docker常用基础命令
文章目录 1、Docker 环境信息命令1.1、docker info1.2、docker version 2、系统日志信息常用命令2.1、docker events2.2、docker logs2.3、docker history 3、容器的生命周期管理命令3.1、docker create3.2、docker run 总结 1、Docker 环境信息命令 1.1、docker info 显示 D…...

自动驾驶中的坐标系
自动驾驶中的坐标系 自动驾驶中的坐标系 0.引言1.相机传感器坐标系2.激光雷达坐标系3.车体坐标系4.世界坐标系4.1.地理坐标系4.2.投影坐标系4.2.1.投影方式4.2.2.墨卡托(Mercator)投影4.2.3.高斯-克吕格(Gauss-Kruger)投影4.2.4.通用横轴墨卡托UTM(UniversalTransve…...

js数组的截取和合并
在JavaScript中,你可以使用slice()方法来截取数组,使用concat()方法来合并数组。 截取数组 slice()方法返回一个新的数组对象,这个对象是一个由原数组的一部分浅复制而来。它接受两个参数,第一个参数是开始截取的位置(…...

2024美赛数学建模思路 - 案例:感知机原理剖析及实现
文章目录 1 感知机的直观理解2 感知机的数学角度3 代码实现 4 建模资料 # 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 感知机的直观理解 感知机应该属于机器学习算法中最简单的一种算法,其…...

大中台,小前台:打造快速响应市场的企业竞争力
2015年,大家都听过“大中台、小前台”战略,听上去很牛。“大中台、小前台”背后完成了一件事情:把阿里巴巴和支付宝所有的基础技术全部统一到阿里云上,这是个重大的技术变革。为了完成这个技术变革,阿里巴巴做了非常好…...

SpringCloud Alibaba 深入源码 - Nacos 和 Eureka 的区别(健康检测、服务的拉取和订阅)
目录 一、Nacos 和 Eureka 的区别 1.1、以 Nacos 注册流程来解析区别 一、Nacos 和 Eureka 的区别 1.1、以 Nacos 注册流程来解析区别 a)首先,我们的服务启动时。都会把自己的信息提交给注册中心,然后注册中心就会把信息保存下来. 注册的…...

Java复习_3
填空题 课程推荐的 jdk 下载网址为 jdk.java.net 使用命令行编译程序:javac -d bin stc*.java 使用命令行运行程序: java -cp bin 类名 java 语言标识符:字母、数字、下划线和美元符号,数字不能做首字母 java 语言中标识符区…...

分类预测 | Matlab实现KPCA-EBWO-SVM分类预测,基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测
分类预测 | Matlab实现KPCA-EBWO-SVM分类预测,基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测 目录 分类预测 | Matlab实现KPCA-EBWO-SVM分类预测,基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测分类效果基本描述程序设计参…...

力扣hot100 找到字符串中所有字母异位词 滑动窗口 双指针 一题双解
Problem: 438. 找到字符串中所有字母异位词 文章目录 思路滑动窗口 数组滑动窗口 双指针 思路 👩🏫 参考题解 滑动窗口 数组 ⏰ 时间复杂度: O ( n ) O(n) O(n) 🌎 空间复杂度: O ( 1 ) O(1) O(1) class Solution { // 滑动窗口 …...

PG DBA培训21:PostgreSQL性能优化之基准测试
本课程由风哥发布的基于PostgreSQL数据库的系列课程,本课程属于PostgreSQL Performance Benchmarking,学完本课程可以掌握PostgreSQL性能基准测试基础知识,基准测试介绍,基准测试相关指标,TPCC基准测试基础,PostgreSQL测试工具介绍,PostgreSQL性能基准测…...

使用excel从1-2048中随机选择1个整数,并展示与其对应的单词
在Excel中,你可以使用以下指令来从1到2048之间随机选择一个整数,并展示其对应的单词: 1. 首先,在一个空白单元格中输入以下公式: INDEX(单词列表范围, RANDBETWEEN(1, 2048)) 这里的"单词列表范围"是一个包…...

c++可调用对象、function类模板与std::bind
函数调用与函数调用运算符 先写一个简单的函数,如下: /*函数的定义*/ int func(int i) {cout<<"这是一个函数\t"<<i<<endl; }void test() {func(1);//函数的调用 } 通过这个普通的函数可以看到,调用一个函数很…...

【高危】Apache Solr 环境变量信息泄漏漏洞
漏洞描述 Apache Solr 是一款开源的搜索引擎。 在 Apache Solr 受影响版本中,由于 Solr Metrics API 默认输出所有未单独配置保护策略的环境变量。在默认无认证或具有 metrics-read 权限的情况下,攻击者可以通过向 /solr/admin/metrics 端点发送恶意请…...

Python中的卷积神经网络(CNN)入门
卷积神经网络(Convolutional Neural Networks, CNN)是一类特别适用于处理图像数据的深度学习模型。在Python中,我们可以使用流行的深度学习库TensorFlow和Keras来创建和训练一个CNN模型。在本文中,我们将介绍如何使用Keras创建一个…...

vulnhub靶机HotelWW
下载地址:https://download.vulnhub.com/worstwesternhotel/HotelWW.ova 主机发现 目标142 端口扫描 服务版本扫描 漏洞扫描 看一下web 好好好这么玩改host 啥也没有先做个目录爆破 扫描太慢我就没看了看几个重点的txt(robot,config,readme&…...

ArcGIS Pro 标注牵引线问题
ArcGIS Pro 标注 模仿CAD坐标牵引线问题 右键需要标注的要素,进入标注属性。 选择背景样式 在这里有可以选择的牵引线样式 选择这一个,可以根据调整间距来进行模仿CAD标注样式。 此图为cad样式 此为调整后gis样式 此处可以调整牵引线的样式符号 …...

Java8的Stream最佳实践
从这一篇文章开始,我们会由浅入深,全面的学习stream API的最佳实践(结合我的使用经验),本想一篇写完,但写着写着发现需要写的内容太多了,所以分成一个系列慢慢来说。给大家分享我的经验的同时&a…...