日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

结合源码深入理解Android Crash处理流程

發布時間:2025/1/21 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 结合源码深入理解Android Crash处理流程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

應用程序crash在開發過程中還是很常見的,本文主要是從源碼的角度去跟蹤下Android對于crash的處理流程。App crash的全稱:Application crash。而Crash又分為:native crash和framework crash(包含App Crash)。我們在平時開發的時候對于可能有異常的地方,一般都是用try-catch語句去catch 異常信息,但當沒有有效的catch住的時候,就會導致應用crash,此時系統就會進行捕獲,并進入到crash的流程。

在《從源碼角度看Android系統Zygote進程啟動過程》一文中可知:system_server進程和上層應用程序的進程都是Zygote進程fork來的,而在這些進程創建的時候會設置未捕獲異常的處理器,當系統中有未捕獲的異常時候,就會交給異常器去處理。

無論是system_server進程還是應用程序進程都會在創建的過程中調用RuntimeInit.java的commonInit方法去做一些通用的初始化操作,其中就有設置默認的未捕捉異常處理器UncaughtHandler。

備注:本文將結合Android6.0的源碼深入理解Android Crash處理流程

1. Android Crash 處理流程

下面以RuntimeInit.java的commonInit方法為起點對Crash流程進行跟蹤分析。

1.1 RuntimeInit.java—>commonInit方法

代碼路徑:frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

深入到commonInit的方法中:

private static final void commonInit() {if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");//設置默認的未捕獲異常處理器Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); //注釋1...省略... }

注釋解析:

  • 注釋1處就是給要創建的進程設置未捕獲異常處理器。setDefaultUncaughtExceptionHandler是將異常處理器的handler對象賦給Thread的成員變量。

下面繼續跟蹤看UncaughtHandler對象實例化的過程!!!

1.2 RuntimeInit.java—>靜態內部類UncaughtHandler

代碼路徑:frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

深入到靜態內部類UncaughtHandler中:

private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {public void uncaughtException(Thread t, Throwable e) {try {//確保crash過程不會重復進入if (mCrashing) return;mCrashing = true;//判斷是否為系統進程if (mApplicationObject == null) {//系統進程Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); //注釋1} else {//普通進程// 注釋2StringBuilder message = new StringBuilder();message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");final String processName = ActivityThread.currentProcessName();if (processName != null) {message.append("Process: ").append(processName).append(", ");}message.append("PID: ").append(Process.myPid());Clog_e(TAG, message.toString(), e);}// 注釋3ActivityManagerNative.getDefault().handleApplicationCrash(mApplicationObject, new ApplicationErrorReport.CrashInfo(e));} catch (Throwable t2) {try {Clog_e(TAG, "Error reporting crash", t2);} catch (Throwable t3) {// Even Clog_e() fails! Oh well.}} finally {// 注釋4Process.killProcess(Process.myPid());System.exit(10);}} }

注釋解析:

  • 注釋1處當是系統進程Crash信息時:

  • 開頭是: *** FATAL EXCEPTION IN SYSTEM PROCESS:[線程名]

  • 輸出發生Crash時的調用棧信息

  • 注釋2處當是普通應用進程時:

  • 開頭是:FATAL EXCEPTION:[線程名]

  • 然后是:Process:[進程名],PID:[進程id]

  • 輸出發生Crash時的調用棧信息

  • 注釋3處是啟動Crash的彈框。而ActivityManagerNative.getDefault()返回的是ActivityManagerProxy(簡稱AMP),AMP經過binder調用最后會交給ActivityManagerService(簡稱AMS),即Crash的彈框最終調用的是AMS的handleApplicationCrash方法。其中傳入的參數new ApplicationErrorReport.CrashInfo(e)的作用是將crash信息文件名、類名、方法名、對應行號和異常信息封裝到CrashInfo對象中。

  • 注釋4處徹底殺掉當前進程

備注:

在平時看Crash 的log時,如果是普通進程,我們只需要搜索關鍵詞“FATAL EXCEPTION:”;如果是系統進程,則需要搜索關鍵詞“*** FATAL EXCEPTION IN SYSTEM PROCESS”

1.3 AMS—>handleApplicationCrash方法

代碼路徑:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到handleApplicationCrash方法中:

public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {// 注釋1ProcessRecord r = findAppProcess(app, "Crash");// 注釋2final String processName = app == null ? "system_server": (r == null ? "unknown" : r.processName);// 注釋3handleApplicationCrashInner("crash", r, processName, crashInfo); }

注釋解析:

  • 注釋1處獲取進程的ProcessRecord對象

  • 注釋2處獲取進程名

  • 當遠程IBinder對象為空時,則進程名為system_server

  • 當遠程IBinder對象不為空時,并且ProcessRecord為空時,則進程名為unknown

  • 當遠程IBinder對象不為空時,并且ProcessRecord不為空時,則進程名為ProcessRecord對象中相應的進程名

  • 注釋3處繼續調用內部方法handleApplicationCrashInner

1.4 AMS—>handleApplicationCrashInner方法

代碼路徑:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到handleApplicationCrashInner方法中:

void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,ApplicationErrorReport.CrashInfo crashInfo) {// 注釋1EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),UserHandle.getUserId(Binder.getCallingUid()), processName,r == null ? -1 : r.info.flags,crashInfo.exceptionClassName,crashInfo.exceptionMessage,crashInfo.throwFileName,crashInfo.throwLineNumber);// 注釋2addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);// 注釋3crashApplication(r, crashInfo); }

注釋解析:

  • 注釋1處將Crash信息寫入到Event Log中

  • 注釋2處將錯誤信息添加到DropBox。其中dropbox的Crash信息是輸出到/data/system/dropbox目錄下,如果是system_server的crash,那么dropbox的文件名就是system_server_crash@xxx.txt(xxx是時間戳)

  • 注釋3處繼續調用內部方法crashApplication

1.5 AMS—>crashApplication方法

代碼路徑:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到crashApplication方法中:

private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {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;}AppErrorResult result = new AppErrorResult();synchronized (this) {...省略...// 注釋1if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {Binder.restoreCallingIdentity(origId);return;}Message msg = Message.obtain();msg.what = SHOW_ERROR_MSG;HashMap data = new HashMap();data.put("result", result);data.put("app", r);msg.obj = data;// 注釋2mUiHandler.sendMessage(msg);}...省略... }

注釋解析:

  • 注釋1處調用makeAppCrashingLocked,繼續處理crash流程

  • 注釋2處發送SHOW_ERROR_MSG,彈出crash提示框,等待用戶選擇

下面繼續深入到makeAppCrashingLocked方法中!!!

1.6 AMS—>makeAppCrashingLocked方法

代碼路徑:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到makeAppCrashingLocked方法中:

private boolean makeAppCrashingLocked(ProcessRecord app,String shortMsg, String longMsg, String stackTrace) {app.crashing = true;// 注釋1app.crashingReport = generateProcessError(app,ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);// 注釋2startAppProblemLocked(app);// 注釋3app.stopFreezingAllLocked();// 注釋4return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace); }

注釋解析:

  • 注釋1處封裝crash信息到crashingReport對象中

  • 注釋2處調用startAppProblemLocked方法,主要功能是獲取當前用戶下crash應用的error receiver和忽略當前app的廣播接受者

  • 注釋3處調用ProcessRecord的stopFreezingAllLocked方法停止屏幕凍結

  • 注釋4處調用handleAppCrashLocked方法繼續處理crash流程

其中注釋1和注釋2處的調用流程不是本文的重點,這里就不繼續跟蹤,大家如果有興趣,可以自行閱讀!后面我會給出一個函數調用鏈,可供參考。

下面繼續跟蹤查看注釋4處的handleAppCrashLocked方法。

1.7 AMS—>handleAppCrashLocked方法

代碼路徑:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到handleAppCrashLocked方法中:

private boolean handleAppCrashLocked(ProcessRecord app, String reason,String shortMsg, String longMsg, String stackTrace) {long now = SystemClock.uptimeMillis();Long crashTime;if (!app.isolated) {crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);} else {crashTime = null;}// 當同一個進程,連續兩次crash的時間間隔小于1分鐘時,則認為crash太過于頻繁if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {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);mStackSupervisor.handleAppCrashLocked(app); //注釋1if (!app.persistent) {EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,app.info.processName);if (!app.isolated) {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;// 移除進程的所有服務removeProcessLocked(app, false, false, "crash"); //注釋2// 恢復最頂部的ActivitymStackSupervisor.resumeTopActivitiesLocked(); //注釋3return false;}mStackSupervisor.resumeTopActivitiesLocked();} else {// 結束最頂端正在運行的ActivitymStackSupervisor.finishTopRunningActivityLocked(app, reason);}// 運行在當前進程中的所有服務的crash次數執行加1操作for (int i=app.services.size()-1; i>=0; i--) {ServiceRecord sr = app.services.valueAt(i);sr.crashCount++;}...省略...// 當app存在crash的handler,那么交給其處理if (app.crashHandler != null) mHandler.post(app.crashHandler); //注釋4return true; }

注釋解析:

  • 注釋1處調用ActivityStackSupervisor的handleAppCrashLocked方法繼續處理crash流程

  • 注釋2處調用內部的removeProcessLocked方法,主要用于移除進程的所有服務

  • 注釋3處調用ActivityStackSupervisor的resumeTopActivitiesLocked方法恢復最頂部的Activity

  • 注釋4處判斷crash的App內部是否有crashHandler,如果有,則交給App內部的crashHandler去處理。這里也證明了《屏蔽Crash 提示框的兩種方式》一文中第二種方案使用crashHandler屏蔽crash提示框的可行性。

邏輯分析流程圖:

其中注釋2和注釋3的流程就不跟蹤了,如果有興趣,自行查閱。后面我會給出一個函數調用鏈,可供參考。

下面跟蹤看下注釋1處的處理流程。

1.8 ActivityStackSupervisor.java—>handleAppCrashLocked方法

代碼路徑:frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

深入到handleAppCrashLocked方法中:

void handleAppCrashLocked(ProcessRecord app) {for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;int stackNdx = stacks.size() - 1;while (stackNdx >= 0) {//調用ActivityStack的handleAppCrashLocked方法stacks.get(stackNdx).handleAppCrashLocked(app);stackNdx--;}} }

繼續深入到ActivityStack.java的handleAppCrashLocked方法中。

代碼路徑:frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

深入到handleAppCrashLocked方法中:

void handleAppCrashLocked(ProcessRecord app) {for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {final ActivityRecord r = activities.get(activityNdx);if (r.app == app) {Slog.w(TAG, " Force finishing activity "+ r.intent.getComponent().flattenToShortString());// Force the destroy to skip right to removal.r.app = null;//結束當前ActivityfinishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);}}} }

到這里AMS中的crashApplication方法中調用的makeAppCrashingLocked方法的流程(上面1.5中的注釋1)就跟蹤完畢!!!

下面繼續看下AMS中的crashApplication方法中UiHandler(上面1.5中的注釋2)

1.9 AMS—>內部類UiHandler

從上面1.5中的注釋2處可知是通過mUiHandler發送message,且消息的msg.what=SHOW_ERROR_MSG,下面來看下mUiHandler中的handleMessage處理過程。

代碼路徑:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入內部類UiHandler中:

final class UiHandler extends Handler {public UiHandler() {super(com.android.server.UiThread.get().getLooper(), null, true);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case SHOW_ERROR_MSG: {HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;synchronized (ActivityManagerService.this) {ProcessRecord proc = (ProcessRecord)data.get("app");AppErrorResult res = (AppErrorResult) data.get("result");if (proc != null && proc.crashDialog != null) {Slog.e(TAG, "App already has crash dialog: " + proc);if (res != null) {res.set(0);}return;}boolean isBackground = (UserHandle.getAppId(proc.uid)>= Process.FIRST_APPLICATION_UID&& proc.pid != MY_PID);for (int userId : mCurrentProfileIds) {isBackground &= (proc.userId != userId);}if (isBackground && !showBackground) {Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");if (res != null) {res.set(0);}return;}if (mShowDialogs && !mSleeping && !mShuttingDown) {//創建crash提示框,等待用戶選擇,等待時間為5分鐘Dialog d = new AppErrorDialog(mContext,ActivityManagerService.this, res, proc);d.show();proc.crashDialog = d;} else {//當處于sleep狀態,則默認選擇退出if (res != null) {res.set(0);}}}ensureBootCompleted();} break;...省略...} }

所以在發生crash時,系統會默認彈出crash提示框,并阻塞等待用戶選擇,當用戶不做任何選擇時,5min后,默認選擇退出。

最后調用上面1.2中注釋4的邏輯,用來殺掉Crash進程。

到此Crash處理流程就跟蹤完畢!!!

2. Android Crash流程總結

當進程拋出未捕獲的異常時,系統就會處理該異常并進入crash處理流程。

2.1 Crash處理流程圖

在流程圖中,最重要的是AMS.handleAppCrashLocked中的功能,里面分了如下兩種情況進行處理:

  • 當同一個進程在1分鐘內連續兩次crash,則執行如下邏輯

    • 非persistent進程

      • 執行ASS.handleAppCrashLock方法,結束該應用所有activity

      • 執行AMS.removeProcessLocked方法,殺死該進程和同一個進程組下的所有進程

      • 執行ASS.resumeTopActivitiesLocked方法,恢復棧頂第一個沒有finishing狀態的activity

    • persistent進程

      • 執行ASS.resumeTopActivitiesLocked方法,恢復棧頂第一個沒有finishing狀態的activity
  • 如果沒有連續crash,則執行如下邏輯

    • 執行ASS.finishTopRunningActivityLocked方法,結束棧頂正在運行activity
  • 2.2 Crash處理流程函數調用鏈
    AMP.handleApplicationCrashAMS.handleApplicationCrashAMS.findAppProcessAMS.handleApplicationCrashInnerAMS.addErrorToDropBoxAMS.crashApplicationAMS.makeAppCrashingLockedAMS.startAppProblemLockedProcessRecord.stopFreezingAllLockedActivityRecord.stopFreezingScreenLockedWMS.stopFreezingScreenLockedWMS.stopFreezingDisplayLockedAMS.handleAppCrashLockedASS.handleAppCrashLockedAS.handleAppCrashLockedAS.finishCurrentActivityLockedAMS.removeProcessLockedProcessRecord.killAMS.handleAppDiedLockedASS.handleAppDiedLockedAMS.cleanUpApplicationRecordLockedAS.handleAppDiedLockedAS.removeHistoryRecordsForAppLockedASS.resumeTopActivitiesLockedAS.resumeTopActivityLockedAS.resumeTopActivityInnerLockedASS.finishTopRunningActivityLockedAS.finishTopRunningActivityLockedAS.finishActivityLockedmUiHandler.sendMessage(SHOW_ERROR_MSG)Process.killProcess(Process.myPid()); System.exit(10);

    其中縮寫對應關系如下:

    AMP--->ActivityManagerProxyAMS--->ActivityManagerServiceWMS--->WindowManagerServiceASS--->ActivityStackSupervisorAS--->ActivityStack
    非常感謝您的耐心閱讀,希望我的文章對您有幫助。歡迎點評、轉發或分享給您的朋友或技術群。
    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的结合源码深入理解Android Crash处理流程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。