当前位置: 首页 > news >正文

稳定性——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 调用栈

  1. KillApplicationHandler.uncaughtException()
    1. stopProfiling
    2. AMS.handleApplicationCrash()
      1. handleApplicationCrashInner
    3. AppErrors.crashApplication()
    4. makeAppCrashingLocked
      1. generateProcessError()
      2. startAppProblemLSP()
        1. getErrorReportReceiver
        2. AMS.skipCurrentReceiverLocked()
      3. stopFreezingActivities()
      4. 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孵化出来的&#xff0c;分为system_server进程和普通应用进程进程创建之初会设置未捕获异常的处理器&#xff0c;当系统抛出未捕获的异常时候都会交给异常处理器RuntimeInit.java的commonInit方法设置UncaughtHandler …...

【控制篇 / 分流】(7.4) ❀ 03. 对国内和国际IP网段访问进行分流 ❀ FortiGate 防火墙

【简介】公司有两条宽带用来上网&#xff0c;一条电信&#xff0c;一条IPLS国际专线&#xff0c;由于IPLS仅有2M&#xff0c;且价格昂贵&#xff0c;领导要求&#xff0c;访问国内IP走电信&#xff0c;国际IP走IPLS&#xff0c;那么应该怎么做&#xff1f; 国内IP地址组 我们已…...

01-开始Rust之旅

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

华南理工大学数字信号处理实验实验一(薛y老师版本)matlab源码

一、实验目的 1、加深对离散信号频谱分析的理解&#xff1b; 2、分析不同加窗长度对信号频谱的影响&#xff1b; 3、理解频率分辨率的概念&#xff0c;并分析其对频谱的 影响&#xff1b; 4、窗长和补零对DFT的影响 实验源码&#xff1a; 第一题&#xff1a; % 定义离散信…...

一篇文章看懂云渲染,云渲染是什么?云渲染如何计费?云渲染怎么选择

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

C++进阶--哈希表模拟实现unordered_set和unordered_map

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

Elasticsearch各种高级文档操作

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

激光无人机打击系统——光束控制和指向系统

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

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 &#xff08;管理员命令行安装&#xff09; pycharm-community-2023.3.2.exe 2 c…...

flask 与小程序 购物车删除和编辑库存功能

编辑 &#xff1a; 数量加减 价格汇总 数据清空 mina/pages/cart/index.wxml <!--index.wxml--> <view class"container"><view class"title-box" wx:if"{{ !list.length }}">购物车空空如也&#xff5e;</view>…...

蓝桥杯真题(Python)每日练Day3

题目 题目分析 为了找到满足条件的放置方法&#xff0c;可以带入总盘数为2和3的情景&#xff0c;用递归做法实现。 2. A中存在1 2两个盘&#xff0c;为了实现最少次数放入C且上小下大&#xff0c;先将1放入B&#xff0c;再将2放入C&#xff0c;最后将1放入C即可。同理当A中存在…...

结构体大揭秘:代码中的时尚之选(上)

目录 结构结构的声明结构成员的类型结构体变量的定义和初始化结构体成员的访问结构体传参 结构 结构是一些值的集合&#xff0c;这些值被称为成员变量。之前说过数组是相同类型元素的集合。结构的每个成员可以是不同类型的变量&#xff0c;当然也可以是相同类型的。 我们在生活…...

【unity学习笔记】语音驱动blendershape

1.导入插件 https://assetstore.unity.com/packages/tools/animation/salsa-lipsync-suite-148442 1.选择小人&#xff0c;点击添加组件 分别加入组件&#xff1a; SALSA EmoteR Eyes Queue Processor&#xff08;必须加此脚本&#xff09;&#xff1a;控制前三个组件的脚本。…...

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&#xff08;UniversalTransve…...

js数组的截取和合并

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

2024美赛数学建模思路 - 案例:感知机原理剖析及实现

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

大中台,小前台:打造快速响应市场的企业竞争力

2015年&#xff0c;大家都听过“大中台、小前台”战略&#xff0c;听上去很牛。“大中台、小前台”背后完成了一件事情&#xff1a;把阿里巴巴和支付宝所有的基础技术全部统一到阿里云上&#xff0c;这是个重大的技术变革。为了完成这个技术变革&#xff0c;阿里巴巴做了非常好…...

SpringCloud Alibaba 深入源码 - Nacos 和 Eureka 的区别(健康检测、服务的拉取和订阅)

目录 一、Nacos 和 Eureka 的区别 1.1、以 Nacos 注册流程来解析区别 一、Nacos 和 Eureka 的区别 1.1、以 Nacos 注册流程来解析区别 a&#xff09;首先&#xff0c;我们的服务启动时。都会把自己的信息提交给注册中心&#xff0c;然后注册中心就会把信息保存下来. 注册的…...

Java复习_3

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

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...