<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
當Android系統發生native crash
時,在紀錄檔臺列印紀錄檔和生成tombstone_xxx檔案,會通過 socket
通知 AMS 從而進入到Java crash側 處理流程中。 同時,當發生Java crash
時,系統會捕捉到該crash,從而也進入到Java crash的處理流程。
由此可見,Java crash
處理流程是非常重要的。 native crash流程上篇文章已經分析過了,今天再來看看Java crash的處理流程。
不管是系統程序還是App程序,啟動的時候都會走到這裡。
RuntimeInit.java
@UnsupportedAppUsage protected static final void commonInit() { if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); LoggingHandler loggingHandler = new LoggingHandler(); RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler); // 註冊處理器 Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler)); }
註冊
殺掉App程序的處理器 KillApplicationHandler
。
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler { private final LoggingHandler mLoggingHandler; /** * Create a new KillApplicationHandler that follows the given LoggingHandler. * If {@link #uncaughtException(Thread, Throwable) uncaughtException} is called * on the created instance without {@code loggingHandler} having been triggered, * {@link LoggingHandler#uncaughtException(Thread, Throwable) * loggingHandler.uncaughtException} will be called first. * * @param loggingHandler the {@link LoggingHandler} expected to have run before * this instance's {@link #uncaughtException(Thread, Throwable) uncaughtException} * is being called. */ public KillApplicationHandler(LoggingHandler loggingHandler) { this.mLoggingHandler = Objects.requireNonNull(loggingHandler); } @Override public void uncaughtException(Thread t, Throwable e) { try { // 在紀錄檔臺列印崩潰時的紀錄檔 ensureLogging(t, e); // Don't re-enter -- avoid infinite loops if crash-reporting crashes. if (mCrashing) return; mCrashing = true; // Try to end profiling. If a profiler is running at this point, and we kill the // process (below), the in-memory buffer will be lost. So try to stop, which will // flush the buffer. (This makes method trace profiling useful to debug crashes.) if (ActivityThread.currentActivityThread() != null) { ActivityThread.currentActivityThread().stopProfiling(); } // Bring up crash dialog, wait for it to be dismissed //彈出奔潰對話方塊 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 { // Try everything to make sure this process goes away. // 最終關閉kill調程序 Process.killProcess(Process.myPid()); System.exit(10); } } private void ensureLogging(Thread t, Throwable e) { if (!mLoggingHandler.mTriggered) { try { mLoggingHandler.uncaughtException(t, e); } catch (Throwable loggingThrowable) { // Ignored. } } } }
職責:
內部呼叫了 LoggingHandler.uncaughtException()
方法。LoggingHandler 也實現了 Thread.UncaughtExceptionHandler介面。 重寫了 uncaughtException() 方法。
private static class LoggingHandler implements Thread.UncaughtExceptionHandler { public volatile boolean mTriggered = false; @Override public void uncaughtException(Thread t, Throwable e) { mTriggered = true; // Don't re-enter if KillApplicationHandler has already run 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())) { Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); } else { // 準備拼接 FATAL EXCEPTION ,列印到控制檯 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. message.append("FATAL EXCEPTION: ").append(t.getName()).append("n"); final String processName = ActivityThread.currentProcessName(); if (processName != null) { // 拼上程序名字 message.append("Process: ").append(processName).append(", "); } // 程序id message.append("PID: ").append(Process.myPid()); // 列印message和 e異常資訊 Clog_e(TAG, message.toString(), e); } } }
拼接 FATAL EXCEPTION
開頭的字串,同時列印崩潰的資訊。
因此,可以通過過濾出 FATAL
EXCEPTION精準定位崩潰的紀錄檔。
new ApplicationErrorReport.ParcelableCrashInfo(e) 建立了一個crashinfo物件。 這個物件其實就是從throwable中 解析得到的。
App端列印了紀錄檔後,就進入到AMS端
的處理邏輯中。
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.ParcelableCrashInfo crashInfo) { //找到 ProcessRecord物件 ProcessRecord r = findAppProcess(app, "Crash"); // app=null,表示system_server程序 final String processName = app == null ? "system_server" : (r == null ? "unknown" : r.processName); handleApplicationCrashInner("crash", r, processName, crashInfo); }
該方法是 RuntimeInit
用來上報app崩潰時呼叫。 當這個方法返回後,App程序將會退出。
handleApplicationCrashInner()
。/* Native crash reporting uses this inner version because it needs to be somewhat * decoupled from the AM-managed cleanup lifecycle */ void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName, ApplicationErrorReport.CrashInfo crashInfo) { // ... 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; } // 1 寫入崩潰資訊到Dropbox addErrorToDropBox( eventType, r, processName, null, null, null, null, null, null, crashInfo); // 2 呼叫mAppErrors 的crashApplication方法 mAppErrors.crashApplication(r, crashInfo); }
這個方法不僅Java crash
回撥,Native crash也會
通過AMS的之前註冊的socket服務,呼叫到這裡。可以參考Native crash流程。
Dropbox
crashApplication()
。把crash、WTF、ANR
的描述寫到drop box中。
public void addErrorToDropBox(String eventType, ProcessRecord process, String processName, String activityShortComponentName, String parentShortComponentName, ProcessRecord parentProcess, String subject, final String report, final File dataFile, final ApplicationErrorReport.CrashInfo crashInfo) { // Bail early if not published yet if (ServiceManager.getService(Context.DROPBOX_SERVICE) == null) return; // 獲取 DBMS服務 final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class); // Exit early if the dropbox isn't configured to accept this report type. // 確定錯誤型別 final String dropboxTag = processClass(process) + "_" + eventType; if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; // Rate-limit how often we're willing to do the heavy lifting below to // collect and record logs; currently 5 logs per 10 second period. final long now = SystemClock.elapsedRealtime(); if (now - mWtfClusterStart > 10 * DateUtils.SECOND_IN_MILLIS) { mWtfClusterStart = now; mWtfClusterCount = 1; } else { if (mWtfClusterCount++ >= 5) return; } // 開始拼接錯誤資訊 final StringBuilder sb = new StringBuilder(1024); appendDropBoxProcessHeaders(process, processName, sb); if (process != null) { // 是否前臺 sb.append("Foreground: ") .append(process.isInterestingToUserLocked() ? "Yes" : "No") .append("n"); } if (activityShortComponentName != null) { sb.append("Activity: ").append(activityShortComponentName).append("n"); } if (parentShortComponentName != null) { if (parentProcess != null && parentProcess.pid != process.pid) { sb.append("Parent-Process: ").append(parentProcess.processName).append("n"); } if (!parentShortComponentName.equals(activityShortComponentName)) { sb.append("Parent-Activity: ").append(parentShortComponentName).append("n"); } } if (subject != null) { sb.append("Subject: ").append(subject).append("n"); } sb.append("Build: ").append(Build.FINGERPRINT).append("n"); if (Debug.isDebuggerConnected()) { sb.append("Debugger: Connectedn"); } if (crashInfo != null && crashInfo.crashTag != null && !crashInfo.crashTag.isEmpty()) { sb.append("Crash-Tag: ").append(crashInfo.crashTag).append("n"); } sb.append("n"); // Do the rest in a worker thread to avoid blocking the caller on I/O // (After this point, we shouldn't access AMS internal data structures.) // dump錯誤資訊 Thread worker = new Thread("Error dump: " + dropboxTag) { @Override public void run() { if (report != null) { sb.append(report); } String setting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag; int lines = Settings.Global.getInt(mContext.getContentResolver(), setting, 0); int maxDataFileSize = DROPBOX_MAX_SIZE - sb.length() - lines * RESERVED_BYTES_PER_LOGCAT_LINE; if (dataFile != null && maxDataFileSize > 0) { try { sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize, "nn[[TRUNCATED]]")); } catch (IOException e) { Slog.e(TAG, "Error reading " + dataFile, e); } } if (crashInfo != null && crashInfo.stackTrace != null) { sb.append(crashInfo.stackTrace); } if (lines > 0) { sb.append("n"); // Merge several logcat streams, and take the last N lines InputStreamReader input = null; try { java.lang.Process logcat = new ProcessBuilder( "/system/bin/timeout", "-k", "15s", "10s", "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system", "-b", "main", "-b", "crash", "-t", String.valueOf(lines)) .redirectErrorStream(true).start(); try { logcat.getOutputStream().close(); } catch (IOException e) {} try { logcat.getErrorStream().close(); } catch (IOException e) {} input = new InputStreamReader(logcat.getInputStream()); int num; char[] buf = new char[8192]; while ((num = input.read(buf)) > 0) sb.append(buf, 0, num); } catch (IOException e) { Slog.e(TAG, "Error running logcat", e); } finally { if (input != null) try { input.close(); } catch (IOException e) {} } } dbox.addText(dropboxTag, sb.toString()); } }; if (process == null) { // If process is null, we are being called from some internal code // and may be about to die -- run this synchronously. final int oldMask = StrictMode.allowThreadDiskWritesMask(); try { // 直接在當前執行緒執行 worker.run(); } finally { StrictMode.setThreadPolicyMask(oldMask); } } else { // 開個新的執行緒執行 worker.start(); } }
dropbox是system-server程序在 StartOtherServices中註冊的服務DropBoxManager。它會記錄系統的關鍵log資訊,用來debug 偵錯。在ServiceManager 中的註冊名字為 dropbox。 dropbox服務的資料儲存在 /data/system/dropbox/中。
dropbox 支援儲存的錯誤型別為:
寫入到Dropbox檔案後,繼續看看 AppErrors.crashApplication()方法:
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); } }
AppErrors.java
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo, int callingPid, int callingUid) { long timeMillis = System.currentTimeMillis(); String shortMsg = crashInfo.exceptionClassName; String longMsg = crashInfo.exceptionMessage; String stackTrace = crashInfo.stackTrace; if (shortMsg != null && longMsg != null) { longMsg = shortMsg + ": " + longMsg; } else if (shortMsg != null) { longMsg = shortMsg; } // ... final int relaunchReason = r != null ? r.getWindowProcessController().computeRelaunchReason() : RELAUNCH_REASON_NONE; AppErrorResult result = new AppErrorResult(); int taskId; synchronized (mService) { // ... // If we can't identify the process or it's already exceeded its crash quota, // quit right away without showing a crash dialog. // 繼續呼叫 makeAppCrashingLocked() if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) { return; } AppErrorDialog.Data data = new AppErrorDialog.Data(); data.result = result; data.proc = r; final Message msg = Message.obtain(); msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG; taskId = data.taskId; msg.obj = data; // 傳送訊息,彈出crash對話方塊,等待使用者選擇 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; } synchronized (mService) { if (res == AppErrorDialog.MUTE) { stopReportingCrashesLocked(r); } // 如果是重新啟動 if (res == AppErrorDialog.RESTART) { mService.mProcessList.removeProcessLocked(r, false, true, "crash"); if (taskId != INVALID_TASK_ID) { try { //1. 從最近的任務列表中找到崩潰程序,再次啟動 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); } } } // 如果是退出 if (res == AppErrorDialog.FORCE_QUIT) { long orig = Binder.clearCallingIdentity(); try { // Kill it with fire! // 殺掉這個程序 mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController()); if (!r.isPersistent()) { mService.mProcessList.removeProcessLocked(r, false, false, "crash"); mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */); } } finally { Binder.restoreCallingIdentity(orig); } } // 如果是顯示應用資訊 if (res == 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); } if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); } if (r != null && !r.isolated && res != AppErrorDialog.RESTART) { // XXX Can't keep track of crash time for isolated processes, // since they don't have a persistent identity. mProcessCrashTimes.put(r.info.processName, r.uid, SystemClock.uptimeMillis()); } } if (appErrorIntent != null) { try { // 2. 啟動一個系統頁面的intent 來顯示應用資訊 mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId)); } catch (ActivityNotFoundException e) { Slog.w(TAG, "bug report receiver dissappeared", e); } } }
職責:
繼續呼叫 makeAppCrashingLocked()
傳送 SHOW_ERROR_UI_MSG
訊息,根據錯誤資訊彈出crash對話方塊
,等待使用者選擇
我們先來看看 makeAppCrashingLocked()
方法:
private boolean makeAppCrashingLocked(ProcessRecord app, String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) { app.setCrashing(true); // 封裝崩潰資訊到 ProcessErrorStateInfo 中 app.crashingReport = generateProcessError(app, ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace); // 獲取當前user的 error receiver;停止廣播接收 app.startAppProblemLocked(); // 停是凍結螢幕 app.getWindowProcessController().stopFreezingActivities(); // 繼續呼叫 handleAppCrashLocked return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace, data); }
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) { // 找到當前使用者的error receiver errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( mService.mContext, info.packageName, info.flags); } } // 停止接收廣播 mService.skipCurrentReceiverLocked(this); } // void skipCurrentReceiverLocked(ProcessRecord app) { for (BroadcastQueue queue : mBroadcastQueues) { queue.skipCurrentReceiverLocked(app); } } private void skipReceiverLocked(BroadcastRecord r) { logBroadcastReceiverDiscardLocked(r); // 停止廣播接收 finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); scheduleBroadcastsLocked(); }
WindowProcessController.java
public void stopFreezingActivities() { synchronized (mAtm.mGlobalLock) { int i = mActivities.size(); while (i > 0) { i--; // mActivities儲存的型別為 ActivityRecord mActivities.get(i).stopFreezingScreenLocked(true); } } }
ActivityRecord.stopFreezingScreenLocked()
ActivityRecord.java
public void stopFreezingScreenLocked(boolean force) { if (force || frozenBeforeDestroy) { frozenBeforeDestroy = false; if (mAppWindowToken == null) { return; } mAppWindowToken.stopFreezingScreen(true, force); } }
最終調到 AMS的 stopFreezingDisplayLocked() 方法來凍結螢幕。
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; } // Bump up the crash count of any services currently running in the proc. // 增加ServiceRecord中crashCount for (int i = app.services.size() - 1; i >= 0; i--) { // Any services running in the application need to be placed // back in the pending list. ServiceRecord sr = app.services.valueAt(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; } } // 同一個程序,如果連續兩次崩潰的間隔小於 一分鐘,則認為崩潰過於頻繁 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); // 2.8.1 回撥 atm的onHandleAppCrash mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController()); if (!app.isPersistent()) { // 如果不是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. if (!app.isolated) { // XXX We don't have a way to mark isolated processes // as bad, since they don't have a peristent identity. mBadProcesses.put(app.info.processName, app.uid, new BadProcessInfo(now, shortMsg, longMsg, stackTrace)); mProcessCrashTimes.remove(app.info.processName, app.uid); } app.bad = true; app.removed = true; // 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, "crash"); // 恢復頂部的activity mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */); if (!showBackground) { return false; } } mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */); } else { // 不是一分鐘內連續崩潰 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; } // 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. 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) { // XXX Can't keep track of crash times for isolated processes, // because they don't have a persistent identity. mProcessCrashTimes.put(app.info.processName, app.uid, now); mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now); } // 如果 app的crashHandler存在,則交給其處理 if (app.crashHandler != null) mService.mHandler.post(app.crashHandler); return true; }
職責:
記錄崩潰之間
增加 ServiceRecord
中crashCount數量
是否是一分鐘內連續崩潰如果是兩次連續崩潰小於一分鐘,則認為是頻繁崩潰。
onHandleAppCrash
方法ATMS.onHandleAppCrash()
ActivityTaskManagerService.java
@Override public void onHandleAppCrash(WindowProcessController wpc) { synchronized (mGlobalLock) { mRootActivityContainer.handleAppCrash(wpc); } } //RootActivityContainer.java void handleAppCrash(WindowProcessController app) { // 遍歷所有的ActivityDisplay for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ActivityDisplay display = mActivityDisplays.get(displayNdx); // 遍歷ActivityDisplay中管理的所有 ActivityStack for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { // 獲取activity stack物件 final ActivityStack stack = display.getChildAt(stackNdx); stack.handleAppCrash(app); } } } >ActivityStack.java void handleAppCrash(WindowProcessController app) { // 迴圈ActivityStack中管理的 TaskRecord for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { // 得到 TaskRecord中管理的所有 ActivityRecord集合 final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; // 遍歷 ActivityRecord集合,得到每一個 ActivityRecord物件 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); // 如果是崩潰的程序,則銷燬activity if (r.app == app) { // Force the destroy to skip right to removal. r.app = null; // getDisplay().mDisplayContent.prepareAppTransition( TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); // finish銷燬當前activity finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false, "handleAppCrashedLocked"); } } } }
職責:
AMS端在收到App的崩潰後,大概流程如下:
crash、wtf、anr
回到3.3.1
中,當處理完 makeAppCrashingLocked()
方法邏輯後,會通過AMS的 mUiHandler
傳送 SHOW_ERROR_UI_MSG 彈出 對話方塊。
AMS.java
final class UiHandler extends Handler { public UiHandler() { super(com.android.server.UiThread.get().getLooper(), null, true); } @Override public void handleMessage(Message msg) { switch (msg.what) { case SHOW_ERROR_UI_MSG: { mAppErrors.handleShowAppErrorUi(msg); ensureBootCompleted(); } break; // ...
AppErrors.java
void handleShowAppErrorUi(Message msg) { AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj; boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; AppErrorDialog dialogToShow = null; final String packageName; final int userId; synchronized (mService) { // 獲取程序資訊 final ProcessRecord proc = data.proc; final AppErrorResult res = data.result; if (proc == null) { Slog.e(TAG, "handleShowAppErrorUi: proc is null"); return; } packageName = proc.info.packageName; userId = proc.userId; // 如果已經有對話方塊,則不再彈出 if (proc.crashDialog != null) { Slog.e(TAG, "App already has crash dialog: " + proc); if (res != null) { res.set(AppErrorDialog.ALREADY_SHOWING); } return; } boolean isBackground = (UserHandle.getAppId(proc.uid) >= Process.FIRST_APPLICATION_UID && proc.pid != MY_PID); for (int profileId : mService.mUserController.getCurrentProfileIds()) { isBackground &= (userId != profileId); } if (isBackground && !showBackground) { Slog.w(TAG, "Skipping crash dialog of " + proc + ": background"); if (res != null) { res.set(AppErrorDialog.BACKGROUND_USER); } return; } final boolean showFirstCrash = Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0; final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, 0, mService.mUserController.getCurrentUserId()) != 0; final boolean crashSilenced = mAppsNotReportingCrashes != null && mAppsNotReportingCrashes.contains(proc.info.packageName); if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground) && !crashSilenced && (showFirstCrash || showFirstCrashDevOption || data.repeating)) { // 建立對話方塊,5分鐘超時等待,超時後自動關閉 proc.crashDialog = dialogToShow = new AppErrorDialog(mContext, mService, data); } else { // The device is asleep, so just pretend that the user // saw a crash dialog and hit "force quit". if (res != null) { res.set(AppErrorDialog.CANT_SHOW); } } } // If we've created a crash dialog, show it without the lock held if (dialogToShow != null) { Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId); // 彈出對話方塊 dialogToShow.show(); } }
邏輯很簡單,就是獲取程序的資訊,並且展示錯誤對話方塊。5分鐘使用者沒有選擇,則自動關閉。
在1和3中都還沒有執行殺應用流程,回顧2.2中的流程,在finally語句中都會走殺程序邏輯。
finally { // Try everything to make sure this process goes away. // 最終關閉kill掉程序 Process.killProcess(Process.myPid()); System.exit(10); }
public static final void killProcess(int pid) { sendSignal(pid, SIGNAL_KILL); } public static final native void sendSignal(int pid, int signal);
給指定的程序傳送一個 SIGNAL_KILL
訊號。具體的殺程序流程,後續再單獨分析。
至此,應用程序已經被殺死,但是還沒完。因為system server程序中有註冊Binder服務的死亡監聽。當App程序死亡後,會回撥到AMS 的死亡監聽中,此時還需要處理Binder死亡通知回撥邏輯。
那麼,AMS是什麼時候註冊死亡通知的呢?
還記得在建立程序的過程中,ActivityThread會呼叫AMS的 attachApplication()
, 內部會呼叫到 attachApplicationLocked()
方法。在這裡註冊的Binder的死亡通知。
@GuardedBy("this") private final boolean attachApplicationLocked(IApplicationThread thread, int pid, int callingUid, long startSeq) { //... try { AppDeathRecipient adr = new AppDeathRecipient( app, pid, thread); thread.asBinder().linkToDeath(adr, 0); app.deathRecipient = adr; } catch (RemoteException e) { app.resetPackageList(mProcessStats); mProcessList.startProcessLocked(app, new HostingRecord("link fail", processName)); return false; } //... }
當有binder服務死亡,會呼叫 AppDeathRecipient 的 binderDied()
方法:
AMS.java
@Override public void binderDied() { if (DEBUG_ALL) Slog.v( TAG, "Death received in " + this + " for thread " + mAppThread.asBinder()); synchronized(ActivityManagerService.this) { appDiedLocked(mApp, mPid, mAppThread, true); } }
@GuardedBy("this") final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread, boolean fromBinderDied) { // First check if this ProcessRecord is actually active for the pid. synchronized (mPidsSelfLocked) { ProcessRecord curProc = mPidsSelfLocked.get(pid); if (curProc != app) { Slog.w(TAG, "Spurious death for " + app + ", curProc for " + pid + ": " + curProc); return; } } BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); synchronized (stats) { stats.noteProcessDiedLocked(app.info.uid, pid); } // 如果沒有被殺,再次殺app if (!app.killed) { if (!fromBinderDied) { killProcessQuiet(pid); } ProcessList.killProcessGroup(app.uid, pid); app.killed = true; } // Clean up already done if the process has been re-started. if (app.pid == pid && app.thread != null && app.thread.asBinder() == thread.asBinder()) { boolean doLowMem = app.getActiveInstrumentation() == null; boolean doOomAdj = doLowMem; if (!app.killedByAm) { reportUidInfoMessageLocked(TAG, "Process " + app.processName + " (pid " + pid + ") has died: " + ProcessList.makeOomAdjString(app.setAdj, true) + " " + ProcessList.makeProcStateString(app.setProcState), app.info.uid); mAllowLowerMemLevel = true; } else { // Note that we always want to do oom adj to update our state with the // new number of procs. mAllowLowerMemLevel = false; doLowMem = false; } // 呼叫 handleAppDiedLocked handleAppDiedLocked(app, false, true); if (doOomAdj) { updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); } if (doLowMem) { doLowMemReportIfNeededLocked(app); } } //... }
final void handleAppDiedLocked(ProcessRecord app, boolean restarting, boolean allowRestart) { int pid = app.pid; // 清理service、broadcastreveiver、contentprovider等資訊 boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1, false /*replacingPid*/); if (!kept && !restarting) { // 移除崩潰程序在AMS中的代表 ProcessRecord removeLruProcessLocked(app); if (pid > 0) { ProcessList.remove(pid); } } if (mProfileData.getProfileProc() == app) { clearProfilerLocked(); } // 繼續呼叫 atm的 handleAppDied mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> { Slog.w(TAG, "Crash of app " + app.processName + " running instrumentation " + app.getActiveInstrumentation().mClass); Bundle info = new Bundle(); info.putString("shortMsg", "Process crashed."); finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info); }); }
該方法清理崩潰程序相關的所有資訊。
final boolean cleanUpApplicationRecordLocked(ProcessRecord app, boolean restarting, boolean allowRestart, int index, boolean replacingPid) { if (index >= 0) { removeLruProcessLocked(app); ProcessList.remove(app.pid); } mProcessesToGc.remove(app); mPendingPssProcesses.remove(app); ProcessList.abortNextPssTime(app.procStateMemTracker); // 關閉所有已經開啟的對話方塊: crash、anr、wait等 // Dismiss any open dialogs. if (app.crashDialog != null && !app.forceCrashReport) { app.crashDialog.dismiss(); app.crashDialog = null; } if (app.anrDialog != null) { app.anrDialog.dismiss(); app.anrDialog = null; } if (app.waitDialog != null) { app.waitDialog.dismiss(); app.waitDialog = null; } app.setCrashing(false); app.setNotResponding(false); app.resetPackageList(mProcessStats); app.unlinkDeathRecipient(); app.makeInactive(mProcessStats); app.waitingToKill = null; app.forcingToImportant = null; updateProcessForegroundLocked(app, false, 0, false); app.setHasForegroundActivities(false); app.hasShownUi = false; app.treatLikeActivity = false; app.hasAboveClient = false; app.setHasClientActivities(false); // 移除所有service 資訊 mServices.killServicesLocked(app, allowRestart); boolean restart = false; // 移除所有的contentprovicer資訊 // Remove published content providers. for (int i = app.pubProviders.size() - 1; i >= 0; i--) { ContentProviderRecord cpr = app.pubProviders.valueAt(i); final boolean always = app.bad || !allowRestart; boolean inLaunching = removeDyingProviderLocked(app, cpr, always); if ((inLaunching || always) && cpr.hasConnectionOrHandle()) { // We left the provider in the launching list, need to // restart it. restart = true; } cpr.provider = null; cpr.setProcess(null); } app.pubProviders.clear(); // Take care of any launching providers waiting for this process. if (cleanupAppInLaunchingProvidersLocked(app, false)) { restart = true; } // Unregister from connected content providers. if (!app.conProviders.isEmpty()) { for (int i = app.conProviders.size() - 1; i >= 0; i--) { ContentProviderConnection conn = app.conProviders.get(i); conn.provider.connections.remove(conn); stopAssociationLocked(app.uid, app.processName, conn.provider.uid, conn.provider.appInfo.longVersionCode, conn.provider.name, conn.provider.info.processName); } app.conProviders.clear(); } // At this point there may be remaining entries in mLaunchingProviders // where we were the only one waiting, so they are no longer of use. // Look for these and clean up if found. // XXX Commented out for now. Trying to figure out a way to reproduce // the actual situation to identify what is actually going on. if (false) { for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) { ContentProviderRecord cpr = mLaunchingProviders.get(i); if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) { synchronized (cpr) { cpr.launchingApp = null; cpr.notifyAll(); } } } } //移除所有的廣播資訊 skipCurrentReceiverLocked(app); // Unregister any receivers. for (int i = app.receivers.size() - 1; i >= 0; i--) { removeReceiverLocked(app.receivers.valueAt(i)); } app.receivers.clear(); //清理App所有的備份 資訊 // If the app is undergoing backup, tell the backup manager about it final BackupRecord backupTarget = mBackupTargets.get(app.userId); if (backupTarget != null && app.pid == backupTarget.app.pid) { if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App " + backupTarget.appInfo + " died during backup"); mHandler.post(new Runnable() { @Override public void run(){ try { IBackupManager bm = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); bm.agentDisconnectedForUser(app.userId, app.info.packageName); } catch (RemoteException e) { // can't happen; backup manager is local } } }); } for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) { ProcessChangeItem item = mPendingProcessChanges.get(i); if (app.pid > 0 && item.pid == app.pid) { mPendingProcessChanges.remove(i); mAvailProcessChanges.add(item); } } mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid, null).sendToTarget(); // If the caller is restarting this app, then leave it in its // current lists and let the caller take care of it. if (restarting) { return false; } if (!app.isPersistent() || app.isolated) { if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Removing non-persistent process during cleanup: " + app); if (!replacingPid) { mProcessList.removeProcessNameLocked(app.processName, app.uid, app); } mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController()); } else if (!app.removed) { // This app is persistent, so we need to keep its record around. // If it is not already on the pending app list, add it there // and start a new process for it. if (mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); restart = true; } } if ((DEBUG_PROCESSES || DEBUG_CLEANUP) && mProcessesOnHold.contains(app)) Slog.v( TAG_CLEANUP, "Clean-up removing on hold: " + app); mProcessesOnHold.remove(app); mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController()); if (restart && !app.isolated) { // We have components that still need to be running in the // process, so re-launch it. if (index < 0) { ProcessList.remove(app.pid); } mProcessList.addProcessNameLocked(app); app.pendingStart = false; mProcessList.startProcessLocked(app, new HostingRecord("restart", app.processName)); return true; } else if (app.pid > 0 && app.pid != MY_PID) { // Goodbye! mPidsSelfLocked.remove(app); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid); if (app.isolated) { mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid); } app.setPid(0); } return false; }
職責:
清理所有跟崩潰程序相關的service、provider、receiver等資訊。
ActivityTaskManagerService.java
@HotPath(caller = HotPath.PROCESS_CHANGE) @Override public void handleAppDied(WindowProcessController wpc, boolean restarting, Runnable finishInstrumentationCallback) { synchronized (mGlobalLockWithoutBoost) { // Remove this application's activities from active lists. // 清理activities相關資訊 boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc); wpc.clearRecentTasks(); wpc.clearActivities(); if (wpc.isInstrumenting()) { finishInstrumentationCallback.run(); } if (!restarting && hasVisibleActivities) { mWindowManager.deferSurfaceLayout(); try { if (!mRootActivityContainer.resumeFocusedStacksTopActivities()) { // If there was nothing to resume, and we are not already restarting // this process, but there is a visible activity that is hosted by the // process...then make sure all visible activities are running, taking // care of restarting this process. // 確保恢復頂部的activity mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } } finally { // windows相關 mWindowManager.continueSurfaceLayout(); } } } }
至此,Binder死亡通知後的處理流程也基本走完,App的整個java crash流程也宣告結束了。
當App發生崩潰後,除了彈出對話方塊,傳送kill命令殺掉自身後。AMS還會收到App程序的Binder服務死亡通知,只有當走完Binder的 binderDied()流程後,整個崩潰流程才算真正結束。
參考:
https://www.jb51.net/article/263031.htm
以上就是Android Java crash 處理流程詳解的詳細內容,更多關於Android Java crash處理流程的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45