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

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

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

發(fā)布時(shí)間:2025/1/21 Android 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 结合源码深入理解Android Crash处理流程 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

應(yīng)用程序crash在開(kāi)發(fā)過(guò)程中還是很常見(jiàn)的,本文主要是從源碼的角度去跟蹤下Android對(duì)于crash的處理流程。App crash的全稱(chēng):Application crash。而Crash又分為:native crash和framework crash(包含App Crash)。我們?cè)谄綍r(shí)開(kāi)發(fā)的時(shí)候?qū)τ诳赡苡挟惓5牡胤?#xff0c;一般都是用try-catch語(yǔ)句去catch 異常信息,但當(dāng)沒(méi)有有效的catch住的時(shí)候,就會(huì)導(dǎo)致應(yīng)用crash,此時(shí)系統(tǒng)就會(huì)進(jìn)行捕獲,并進(jìn)入到crash的流程。

在《從源碼角度看Android系統(tǒng)Zygote進(jìn)程啟動(dòng)過(guò)程》一文中可知:system_server進(jìn)程和上層應(yīng)用程序的進(jìn)程都是Zygote進(jìn)程fork來(lái)的,而在這些進(jìn)程創(chuàng)建的時(shí)候會(huì)設(shè)置未捕獲異常的處理器,當(dāng)系統(tǒng)中有未捕獲的異常時(shí)候,就會(huì)交給異常器去處理。

無(wú)論是system_server進(jìn)程還是應(yīng)用程序進(jìn)程都會(huì)在創(chuàng)建的過(guò)程中調(diào)用RuntimeInit.java的commonInit方法去做一些通用的初始化操作,其中就有設(shè)置默認(rèn)的未捕捉異常處理器UncaughtHandler。

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

1. Android Crash 處理流程

下面以RuntimeInit.java的commonInit方法為起點(diǎn)對(duì)Crash流程進(jìn)行跟蹤分析。

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!");//設(shè)置默認(rèn)的未捕獲異常處理器Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); //注釋1...省略... }

注釋解析:

  • 注釋1處就是給要?jiǎng)?chuàng)建的進(jìn)程設(shè)置未捕獲異常處理器。setDefaultUncaughtExceptionHandler是將異常處理器的handler對(duì)象賦給Thread的成員變量。

下面繼續(xù)跟蹤看UncaughtHandler對(duì)象實(shí)例化的過(guò)程!!!

1.2 RuntimeInit.java—>靜態(tài)內(nèi)部類(lèi)UncaughtHandler

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

深入到靜態(tài)內(nèi)部類(lèi)UncaughtHandler中:

private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {public void uncaughtException(Thread t, Throwable e) {try {//確保crash過(guò)程不會(huì)重復(fù)進(jìn)入if (mCrashing) return;mCrashing = true;//判斷是否為系統(tǒng)進(jìn)程if (mApplicationObject == null) {//系統(tǒng)進(jìn)程Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); //注釋1} else {//普通進(jìn)程// 注釋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處當(dāng)是系統(tǒng)進(jìn)程Crash信息時(shí):

  • 開(kāi)頭是: *** FATAL EXCEPTION IN SYSTEM PROCESS:[線(xiàn)程名]

  • 輸出發(fā)生Crash時(shí)的調(diào)用棧信息

  • 注釋2處當(dāng)是普通應(yīng)用進(jìn)程時(shí):

  • 開(kāi)頭是:FATAL EXCEPTION:[線(xiàn)程名]

  • 然后是:Process:[進(jìn)程名],PID:[進(jìn)程id]

  • 輸出發(fā)生Crash時(shí)的調(diào)用棧信息

  • 注釋3處是啟動(dòng)Crash的彈框。而ActivityManagerNative.getDefault()返回的是ActivityManagerProxy(簡(jiǎn)稱(chēng)AMP),AMP經(jīng)過(guò)binder調(diào)用最后會(huì)交給ActivityManagerService(簡(jiǎn)稱(chēng)AMS),即Crash的彈框最終調(diào)用的是AMS的handleApplicationCrash方法。其中傳入的參數(shù)new ApplicationErrorReport.CrashInfo(e)的作用是將crash信息文件名、類(lèi)名、方法名、對(duì)應(yīng)行號(hào)和異常信息封裝到CrashInfo對(duì)象中。

  • 注釋4處徹底殺掉當(dāng)前進(jìn)程

備注:

在平時(shí)看Crash 的log時(shí),如果是普通進(jìn)程,我們只需要搜索關(guān)鍵詞“FATAL EXCEPTION:”;如果是系統(tǒng)進(jìn)程,則需要搜索關(guān)鍵詞“*** 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處獲取進(jìn)程的ProcessRecord對(duì)象

  • 注釋2處獲取進(jìn)程名

  • 當(dāng)遠(yuǎn)程IBinder對(duì)象為空時(shí),則進(jìn)程名為system_server

  • 當(dāng)遠(yuǎn)程IBinder對(duì)象不為空時(shí),并且ProcessRecord為空時(shí),則進(jìn)程名為unknown

  • 當(dāng)遠(yuǎn)程IBinder對(duì)象不為空時(shí),并且ProcessRecord不為空時(shí),則進(jìn)程名為ProcessRecord對(duì)象中相應(yīng)的進(jìn)程名

  • 注釋3處繼續(xù)調(diào)用內(nèi)部方法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信息寫(xiě)入到Event Log中

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

  • 注釋3處繼續(xù)調(diào)用內(nèi)部方法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處調(diào)用makeAppCrashingLocked,繼續(xù)處理crash流程

  • 注釋2處發(fā)送SHOW_ERROR_MSG,彈出crash提示框,等待用戶(hù)選擇

下面繼續(xù)深入到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對(duì)象中

  • 注釋2處調(diào)用startAppProblemLocked方法,主要功能是獲取當(dāng)前用戶(hù)下crash應(yīng)用的error receiver和忽略當(dāng)前app的廣播接受者

  • 注釋3處調(diào)用ProcessRecord的stopFreezingAllLocked方法停止屏幕凍結(jié)

  • 注釋4處調(diào)用handleAppCrashLocked方法繼續(xù)處理crash流程

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

下面繼續(xù)跟蹤查看注釋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;}// 當(dāng)同一個(gè)進(jìn)程,連續(xù)兩次crash的時(shí)間間隔小于1分鐘時(shí),則認(rèn)為crash太過(guò)于頻繁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;// 移除進(jìn)程的所有服務(wù)removeProcessLocked(app, false, false, "crash"); //注釋2// 恢復(fù)最頂部的ActivitymStackSupervisor.resumeTopActivitiesLocked(); //注釋3return false;}mStackSupervisor.resumeTopActivitiesLocked();} else {// 結(jié)束最頂端正在運(yùn)行的ActivitymStackSupervisor.finishTopRunningActivityLocked(app, reason);}// 運(yùn)行在當(dāng)前進(jìn)程中的所有服務(wù)的crash次數(shù)執(zhí)行加1操作for (int i=app.services.size()-1; i>=0; i--) {ServiceRecord sr = app.services.valueAt(i);sr.crashCount++;}...省略...// 當(dāng)app存在crash的handler,那么交給其處理if (app.crashHandler != null) mHandler.post(app.crashHandler); //注釋4return true; }

注釋解析:

  • 注釋1處調(diào)用ActivityStackSupervisor的handleAppCrashLocked方法繼續(xù)處理crash流程

  • 注釋2處調(diào)用內(nèi)部的removeProcessLocked方法,主要用于移除進(jìn)程的所有服務(wù)

  • 注釋3處調(diào)用ActivityStackSupervisor的resumeTopActivitiesLocked方法恢復(fù)最頂部的Activity

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

邏輯分析流程圖:

其中注釋2和注釋3的流程就不跟蹤了,如果有興趣,自行查閱。后面我會(huì)給出一個(gè)函數(shù)調(diào)用鏈,可供參考。

下面跟蹤看下注釋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) {//調(diào)用ActivityStack的handleAppCrashLocked方法stacks.get(stackNdx).handleAppCrashLocked(app);stackNdx--;}} }

繼續(xù)深入到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;//結(jié)束當(dāng)前ActivityfinishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);}}} }

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

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

1.9 AMS—>內(nèi)部類(lèi)UiHandler

從上面1.5中的注釋2處可知是通過(guò)mUiHandler發(fā)送message,且消息的msg.what=SHOW_ERROR_MSG,下面來(lái)看下mUiHandler中的handleMessage處理過(guò)程。

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

深入內(nèi)部類(lèi)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) {//創(chuàng)建crash提示框,等待用戶(hù)選擇,等待時(shí)間為5分鐘Dialog d = new AppErrorDialog(mContext,ActivityManagerService.this, res, proc);d.show();proc.crashDialog = d;} else {//當(dāng)處于sleep狀態(tài),則默認(rèn)選擇退出if (res != null) {res.set(0);}}}ensureBootCompleted();} break;...省略...} }

所以在發(fā)生crash時(shí),系統(tǒng)會(huì)默認(rèn)彈出crash提示框,并阻塞等待用戶(hù)選擇,當(dāng)用戶(hù)不做任何選擇時(shí),5min后,默認(rèn)選擇退出。

最后調(diào)用上面1.2中注釋4的邏輯,用來(lái)殺掉Crash進(jìn)程。

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

2. Android Crash流程總結(jié)

當(dāng)進(jìn)程拋出未捕獲的異常時(shí),系統(tǒng)就會(huì)處理該異常并進(jìn)入crash處理流程。

2.1 Crash處理流程圖

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

  • 當(dāng)同一個(gè)進(jìn)程在1分鐘內(nèi)連續(xù)兩次crash,則執(zhí)行如下邏輯

    • 非persistent進(jìn)程

      • 執(zhí)行ASS.handleAppCrashLock方法,結(jié)束該應(yīng)用所有activity

      • 執(zhí)行AMS.removeProcessLocked方法,殺死該進(jìn)程和同一個(gè)進(jìn)程組下的所有進(jìn)程

      • 執(zhí)行ASS.resumeTopActivitiesLocked方法,恢復(fù)棧頂?shù)谝粋€(gè)沒(méi)有finishing狀態(tài)的activity

    • persistent進(jìn)程

      • 執(zhí)行ASS.resumeTopActivitiesLocked方法,恢復(fù)棧頂?shù)谝粋€(gè)沒(méi)有finishing狀態(tài)的activity
  • 如果沒(méi)有連續(xù)crash,則執(zhí)行如下邏輯

    • 執(zhí)行ASS.finishTopRunningActivityLocked方法,結(jié)束棧頂正在運(yùn)行activity
  • 2.2 Crash處理流程函數(shù)調(diào)用鏈
    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);

    其中縮寫(xiě)對(duì)應(yīng)關(guān)系如下:

    AMP--->ActivityManagerProxyAMS--->ActivityManagerServiceWMS--->WindowManagerServiceASS--->ActivityStackSupervisorAS--->ActivityStack
    非常感謝您的耐心閱讀,希望我的文章對(duì)您有幫助。歡迎點(diǎn)評(píng)、轉(zhuǎn)發(fā)或分享給您的朋友或技術(shù)群。
    與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖

    總結(jié)

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

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。