JE crash處理流程

Jave Crash 處理流程

[基於 android P]

一、概述

App crash(全稱Application crash), 對於Crash可分爲native crash和java crash,對於crash相信很多app開發者都會遇到,那麼上層什麼時候會出現crash呢,系統又是如何處理crash的呢。例如,在app大家經常使用try…catch語句,那麼如果沒有有效catch exception,就是導致應用crash,系統便會來捕獲,並進入crash流程。

我們知道上層應用進程都是由Zygote fork而來,分爲system_server系統進程和各種應用進程,在這些進程創建之初會設置未捕獲異常的處理器,當系統拋出未捕獲的異常時,最終都交給異常處理器,即RuntimeInit.java的commonInit方法設置UncaughtHandler來捕獲異常。

crash處理流程的堆棧調用關係:

RuntimeInit.setDefaultUncaughtExceptionHandler
	RuntimeInit.KillApplicationHandler.uncaughtException
		AM.getService().handleApplicationCrash(binder通信)
		    AMS.handleApplicationCrash
		        AMS.findAppProcess
		        AMS.handleApplicationCrashInner
		            AMS.addErrorToDropBox
		            AppErr.crashApplication
						AppErr.crashApplicationInner
		                	AMS.makeAppCrashingLocked
		                    	AMS.startAppProblemLocked
		                    	ProcessRecord.stopFreezingAllLocked
		                        	ActivityRecord.stopFreezingScreenLocked
		                                WMS.stopFreezingDisplayLocked
		                    	AMS.handleAppCrashLocked
		                	mUiHandler.sendMessage(SHOW_ERROR_UI_MSG)
		
		Process.killProcess(Process.myPid());
		System.exit(10);

二、Crash處理流程

那麼接下來以commonInit()方法爲起點來梳理流程。

1. RuntimeInit.commonInit

public class RuntimeInit {
    ...
    private static final void commonInit() {
        //設置默認的未捕獲異常處理器,UncaughtHandler實例化過程
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
        ...
    }
}

setDefaultUncaughtExceptionHandler()只是將異常處理器handler對象賦給Thread成員變量,接下來看看KillApplicationHandler對象實例化過程。

2. UncaughtHandler

[–>RuntimeInit.java]

private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler{
    //覆寫接口方法
    public void uncaughtException(Thread t, Throwable e) {
		try {
            ensureLogging(t, e);//【2.1】
            ......
            // 【3】
            ActivityManager.getService().handleApplicationCrash(
                    mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
        } catch (Throwable t2) {
            ......
        } finally {
            // 確保進程被殺死【11】
            Process.killProcess(Process.myPid());
            System.exit(10);
        }
        
}
2.1 ensureLogging

[–> RuntimeInit.java]

private void ensureLogging(Thread t, Throwable e) {
    if (!mLoggingHandler.mTriggered) {
        try {
			//【2.2】
            mLoggingHandler.uncaughtException(t, e);
        } catch (Throwable loggingThrowable) {
            // Ignored.
        }
    }
}
2.2 uncaughtException

[-> RuntimeInit.LoggingHandler]

private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
    public volatile boolean mTriggered = false;

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        mTriggered = true;
        if (mCrashing) return;
		//注意: mApplicationObject等於null,一定不是普通的app進程. 
		//除了system進程, 也有可能是shell進程, 即通過app_process + 命令參數 的方式創建的進程。
        if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
            Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
        } else {
            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(", ");
            }
            message.append("PID: ").append(Process.myPid());
            Clog_e(TAG, message.toString(), e);
        }
    }
}

當system進程crash的信息:
開頭*** FATAL EXCEPTION IN SYSTEM PROCESS [線程名];
接着輸出發生crash時的調用棧信息;

當app進程crash時的信息:
開頭FATAL EXCEPTION: [線程名];
緊接着 Process: [進程名], PID: [進程id];
最後輸出發生crash時的調用棧信息,
app crash 保存在crash_log中。

當輸出完crash信息到logcat裏面,這只是crash流程的剛開始階段,接下來彈出crash對話框,經過binder調用最終交給ActivityManagerService(簡稱AMS)中相應的方法去處理,故接下來調用的是AMS.handleApplicationCrash()。

3. handleApplicationCrash

[–>ActivityManagerService.java]

public void handleApplicationCrash(IBinder app, ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
    //獲取Processrecord對象 【3.1】
    ProcessRecord r = findAppProcess(app, "Crash");
    final String processName = app == null ? "system_server"
            : (r == null ? "unknown" : r.processName);
    //【4】
    handleApplicationCrashInner("crash", r, processName, crashInfo);
}

關於進程名(processName):

  • 當遠程IBinder對象爲空時,則進程名爲system_server;
  • 當遠程IBinder對象不爲空,且ProcessRecord爲空時,則進程名爲unknown;
  • 當遠程IBinder對象不爲空,且ProcessRecord不爲空時,則進程名爲ProcessRecord對象中相應進程名。
3.1 findAppProcess

[–>ActivityManagerService.java]

private ProcessRecord findAppProcess(IBinder app, String reason) {
    if (app == null) {
        return null;
    }

    synchronized (this) {
        final int NP = mProcessNames.getMap().size();
        for (int ip=0; ip<NP; ip++) {
            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
            final int NA = apps.size();
            for (int ia=0; ia<NA; ia++) {
                ProcessRecord p = apps.valueAt(ia);
                //當找到目標進程則返回
                if (p.thread != null && p.thread.asBinder() == app) {
                    return p;
                }
            }
        }
        //如果代碼執行到這裏,表明無法找到應用所在的進程
        return null;
    }
}

其中 mProcessNames = new ProcessMap();對於代碼mProcessNames.getMap()返回的是mMap,而mMap= new ArrayMap<String, SparseArray>();

知識延伸:SparseArray和ArrayMap是Android專門針對內存優化而設計的取代Java API中的HashMap的數據結構。對於key是int類型則使用SparseArray,可避免自動裝箱過程;對於key爲其他類型則使用ArrayMap。HashMap的查找和插入時間複雜度爲O(1)的代價是犧牲大量的內存來實現的,而SparseArray和ArrayMap性能略遜於HashMap,但更節省內存。

再回到mMap,這是以進程name爲key,再以(uid爲key,以ProcessRecord爲Value的)結構體作爲value。

有了進程記錄對象ProcessRecord和進程名processName,則進入執行Crash處理方法。

4. handleApplicationCrashInner

[–>ActivityManagerService.java]

void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
        ApplicationErrorReport.CrashInfo crashInfo) {
    //將Crash信息寫入到Event log
    EventLog.writeEvent(EventLogTags.AM_CRASH,...);
	.....
    //錯誤信息封裝,並寫入手機存儲
    addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
    //【5】
    mAppErrors.crashApplication(r, crashInfo);
}

其中addErrorToDropBox是將crash的信息輸出到目錄/data/system/dropbox。例如system_server的dropbox文件名爲[email protected] (xxx代表的是時間戳)

5. crashApplication&crashApplicationInner

[–>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);
    }
}

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;
    }

    AppErrorResult result = new AppErrorResult();
    TaskRecord task;
    synchronized (mService) {
        ......

        AppErrorDialog.Data data = new AppErrorDialog.Data();
        data.result = result;
        data.proc = r;

        // If we can't identify the process or it's already exceeded its crash quota,
        // quit right away without showing a crash dialog. 【6】
        if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
            return;
        }

        final Message msg = Message.obtain();
        msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;

        task = data.task;
        msg.obj = data;
		//發送顯示crash dialog message
        mService.mUiHandler.sendMessage(msg);
    }
	//根據彈出框用戶選擇結果做不同的操作
    int res = result.get();
    ....
}

該方法主要做的3件事:

  1. 調用makeAppCrashingLocked,繼續處理crash流程;
  2. 發送消息SHOW_ERROR_UI_MSG,彈出提示crash的對話框,等待用戶選擇;
  3. 根據用戶操作,執行不同的操作,kill Process或者發送錯誤報告等。

6. makeAppCrashingLocked

[–>ActivityManagerService.java]

private boolean makeAppCrashingLocked(ProcessRecord app, String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
    app.crashing = true;
    //封裝crash信息到crashingReport對象
    app.crashingReport = generateProcessError(app,
            ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
    //【7】停止當前進程的廣播接收
    startAppProblemLocked(app);
    //停止屏幕凍結 @-8
    app.stopFreezingAllLocked();
    //【9】
    return handleAppCrashLocked(app, "force-crash", shortMsg, longMsg, stackTrace,data);
}

7. startAppProblemLocked

[–>ActivityManagerService.java]

void startAppProblemLocked(ProcessRecord app) {
    app.errorReportReceiver = null;

    for (int userId : mCurrentProfileIds) {
        if (app.userId == userId) {
            //獲取當前用戶下的crash應用的error receiver  @-7.1
            app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
                    mContext, app.info.packageName, app.info.flags);
        }
    }
    //忽略當前app的廣播接收 @-7.2
     mService.skipCurrentReceiverLocked(app);
}

該方法主要功能:

  1. 獲取當前用戶下的crash應用的error receiver;
  2. 忽略當前app的廣播接收;
7.1 getErrorReportReceiver

[-> ApplicationErrorReport.java]

public static ComponentName getErrorReportReceiver(Context context,
        String packageName, int appFlags) {
    //檢查Settings中的"send_action_app_error"是否使能錯誤報告的功能
    int enabled = Settings.Global.getInt(context.getContentResolver(),
            Settings.Global.SEND_ACTION_APP_ERROR, 0);
    if (enabled == 0) {
        //1.當未使能時,則直接返回
        return null;
    }

    PackageManager pm = context.getPackageManager();

    String candidate = null;
    ComponentName result = null;
    try {
        //獲取該crash應用的安裝器的包名
        candidate = pm.getInstallerPackageName(packageName);
    } catch (IllegalArgumentException e) {
    }

    if (candidate != null) {
        result = getErrorReportReceiver(pm, packageName, candidate);//【見下文】
        if (result != null) {
            //2.當找到該crash應用的安裝器,則返回;
            return result;
        }
    }

    if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
        //該系統屬性名爲"ro.error.receiver.system.apps"
        candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
        result = getErrorReportReceiver(pm, packageName, candidate);//【見下文】
        if (result != null) {
            //3.當crash應用是系統應用時,且系統屬性指定error receiver時,則返回;
            return result;
        }
    }

    //該默認屬性名爲"ro.error.receiver.default"
    candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
    //4.當默認屬性值指定error receiver時,則返回;
    return getErrorReportReceiver(pm, packageName, candidate); //下面構造方法
}

getErrorReportReceiver:這是一個重載方法方法:

static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
        String receiverPackage) {
    if (receiverPackage == null || receiverPackage.length() == 0) {
        return null;
    }

    //當安裝應用程序的安裝器Crash,則直接返回
    if (receiverPackage.equals(errorPackage)) {
        return null;
    }

    //ACTION_APP_ERROR值爲"android.intent.action.APP_ERROR"
    Intent intent = new Intent(Intent.ACTION_APP_ERROR);
    intent.setPackage(receiverPackage);
    ResolveInfo info = pm.resolveActivity(intent, 0);
    if (info == null || info.activityInfo == null) {
        return null;
    }
    //創建包名爲receiverPackage的組件
    return new ComponentName(receiverPackage, info.activityInfo.name);
}
7.2 skipCurrentReceiverLocked

[–>ActivityManagerService.java]

void skipCurrentReceiverLocked(ProcessRecord app) {
    for (BroadcastQueue queue : mBroadcastQueues) {
        queue.skipCurrentReceiverLocked(app);  // 【7.3】
    }
}
7.3 skipCurrentReceiverLocked

[-> BroadcastQueue.java]

public void skipCurrentReceiverLocked(ProcessRecord app) {
    BroadcastRecord r = null;
	//檢測是否含有有序廣播
    if (mOrderedBroadcasts.size() > 0) {
        BroadcastRecord br = mOrderedBroadcasts.get(0);
        if (br.curApp == app) {
            r = br;
        }
    }
	//檢測當前pendingBroadCast進程是否是當前進程
    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) {
		//【7.4】
        skipReceiverLocked(r);
    }
}
7.4 skipReceiverLocked

[-> BroadcastQueue.java]

private void skipReceiverLocked(BroadcastRecord r) {
    logBroadcastReceiverDiscardLocked(r);
	//取消廣播接收
    finishReceiverLocked(r, r.resultCode, r.resultData,
            r.resultExtras, r.resultAbort, false);
    scheduleBroadcastsLocked();
}

8. PR.stopFreezingAllLocked

[-> ProcessRecord.java]

public void stopFreezingAllLocked() {
    int i = activities.size();
    while (i > 0) {
        i--;
        activities.get(i).stopFreezingScreenLocked(true); //【8.1】
    }
}

其中activities類型爲ArrayList,停止進程裏所有的Activity

8.1. AR.stopFreezingScreenLocked

[-> ActivityRecord.java]

public void stopFreezingScreenLocked(boolean force) {
    if (force || frozenBeforeDestroy) {
        frozenBeforeDestroy = false;
        mWindowContainerController.stopFreezingScreen(force);
    }
}
8.2. AWCC.stopFreezingScreenLocked

[-> AppWindowContainerController.java]

public void stopFreezingScreen(boolean force) {
    synchronized(mWindowMap) {
        if (mContainer == null) {
            return;
        }
        mContainer.stopFreezingScreen(true, force);
    }
}

其中mContainer是appWindowToken對象,故調用AppWindowToken的stopFreezingScreen方法,並且持WMS鎖。

8.3 AWT.stopFreezingScreen

[-> AppWindowToken.java]

void stopFreezingScreen(boolean unfreezeSurfaceNow, boolean force) {
    if (!mFreezingScreen) {
        return;
    }
    if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + this + " force=" + force);
    final int count = mChildren.size();
    boolean unfrozeWindows = false;
    for (int i = 0; i < count; i++) {
        final WindowState w = mChildren.get(i);
        unfrozeWindows |= w.onStopFreezingScreen();
    }
    if (force || unfrozeWindows) {
        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
        mFreezingScreen = false;
        mService.unregisterAppFreezeListener(this);
        mService.mAppsFreezingScreen--;
        mService.mLastFinishedFreezeSource = this;
    }
    if (unfreezeSurfaceNow) {
        if (unfrozeWindows) {
            mService.mWindowPlacerLocked.performSurfacePlacement();
        }
        mService.stopFreezingDisplayLocked();
    }
}

unfreezeSurfaceNow由上一方法傳入爲true,所以最後調用WMS的stopFreezingDisplayLocked.

8.4 WMS.stopFreezingDisplayLocked

[-> WindowManagerService.java]

private void stopFreezingDisplayLocked() {
    if (!mDisplayFrozen) {
        return; //顯示沒有凍結,則直接返回
    }

    //往往跟屏幕旋轉相關
    ...
    final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);

    final int displayId = mFrozenDisplayId;
    mFrozenDisplayId = INVALID_DISPLAY;
    mDisplayFrozen = false;
	//使能輸入事件分發功能
    mInputMonitor.thawInputDispatchingLw();
    //從上次凍屏到現在的總時長
    mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);

    //移除凍屏的超時消息
    mH.removeMessages(H.APP_FREEZE_TIMEOUT);
    mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);

    boolean updateRotation = false;

    ScreenRotationAnimation screenRotationAnimation =
            mAnimator.getScreenRotationAnimationLocked(displayId);
	//加載屏幕旋轉的動畫
    if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
            && screenRotationAnimation.hasScreenshot()) {
        if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
        // TODO(multidisplay): rotation on main screen only.
        DisplayInfo displayInfo = displayContent.getDisplayInfo();
        // Get rotation animation again, with new top window
        if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
            mExitAnimId = mEnterAnimId = 0;
        }
        if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
                getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                    displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
            mTransaction.apply();
            scheduleAnimationLocked();
        } else {
            screenRotationAnimation.kill();
            mAnimator.setScreenRotationAnimationLocked(displayId, null);
            updateRotation = true;
        }
    } else {
        if (screenRotationAnimation != null) {
            screenRotationAnimation.kill();
            mAnimator.setScreenRotationAnimationLocked(displayId, null);
        }
        updateRotation = true;
    }

    boolean configChanged;
    //當display被凍結時不再計算屏幕方向,以避免不連續的狀態。
    configChanged = updateOrientationFromAppTokensLocked(false);

    //display凍結時,執行gc操作
    mH.removeMessages(H.FORCE_GC);
    mH.sendEmptyMessageDelayed(H.FORCE_GC, 2000);

    //mScreenFrozenLock的類型爲PowerManager.WakeLock,即釋放屏幕凍結的鎖
    mScreenFrozenLock.release();

    if (updateRotation) {
        //更新當前的屏幕方向
        configChanged |= updateRotationUncheckedLocked(false);
    }

	if (configChanged) {
        //向mH發送configuraion改變的消息
        mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
    }
    mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
}

該方法主要功能:

  1. 處理屏幕旋轉相關邏輯;
  2. 使能輸入事件分發功能;
  3. 移除凍屏的超時消息;
  4. 屏幕旋轉動畫的相關操作;
  5. display凍結時,執行gc操作;
  6. 更新當前的屏幕方向;
  7. 向mH發送configuraion改變的消息。

9.AMS.handleAppCrashLocked

[-> ActivityManagerService.java]

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.curProcState == 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 = null;
    }

    //統計當前在proc中運行的任何服務的Crash計數。
    for (int i = app.services.size() - 1; i >= 0; i--) {
        ////需要將應用程序中運行的任何服務放回到掛起列表中
        ServiceRecord sr = app.services.valueAt(i);
        //如果服務剛剛重啓,則重置崩潰計數,否則增加它。
        if (now > sr.restartTime + ProcessList.MIN_CRASH_INTERVAL) {
            sr.crashCount = 1;
        } else {
            sr.crashCount++;
        }
        //允許重新啓動正在Crash的已啓動或綁定的前臺服務。包括壁紙
        if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
                && (sr.isForeground || procIsBoundForeground)) {
            tryAgain = true;
        }
    }

    //當同一個進程,連續兩次crash的時間間隔小於1分鐘時,則認爲crash太過於頻繁
    if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
        EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
                app.userId, app.info.processName, app.uid);
        //【9.1】
        mStackSupervisor.handleAppCrashLocked(app);
        if (!app.persistent) {
            //不再重啓非persistent進程,除非用戶顯式地調用
            EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
                    app.info.processName);
            if (!app.isolated) {
                //將當前app加入到mBadProcesses
                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;
            //移除進程的所有服務,保證不再重啓【9.2】
            mService.removeProcessLocked(app, false, tryAgain, "crash");
            //恢復最頂部的Activity【9.3】
            mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
            if (!showBackground) {
                 return false;
            }
        }
        mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
    } else {
        //此處reason="force-crash"【9.4】
        final TaskRecord affectedTask =
             mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app, reason);
        if (data != null) {
            data.task = affectedTask;
        }
        if (data != null && crashTimePersistent != null
                && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
            data.repeating = true;
        }
    }
    if (data != null && tryAgain) {
        data.isRestartableForService = true;
    }

    //當桌面應用crash,並且被三方app所取代,那麼需要清空桌面應用的偏愛選項。
    final ArrayList<ActivityRecord> activities = app.activities;
    if (app == mHomeProcess && activities.size() > 0
                && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
        for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
            final ActivityRecord r = activities.get(activityNdx);
            if (r.isHomeActivity()) {
                //清空偏愛應用
                try {
                    ActivityThread.getPackageManager()
                            .clearPackagePreferredActivities(r.packageName);
                } catch (RemoteException c) {
                    // pm is in same process, this will never happen.
                }
            }
        }
    }

    if (!app.isolated) {
        //無法跟蹤隔離進程的崩潰時間,因爲它們沒有持久標識
        mProcessCrashTimes.put(app.info.processName, app.uid, now);
        mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now);
    }
    //當app存在crash的handler,那麼交給其處理
    if (app.crashHandler != null) mHandler.post(app.crashHandler);
    return true;
}

當同一進程在時間間隔小於1分鐘時連續兩次crash,則執行的情況下:

對於非persistent進程:

  • [9.1] mStackSupervisor.handleAppCrashLocked(app);
  • [9.2] removeProcessLocked();//調用app.kill 殺進程。
  • [9.3] mStackSupervisor.resumeTopActivitiesLocked();
    對於persistent進程,則只執行mStackSupervisor.resumeTopActivitiesLocked();
    否則執行 mStackSupervisor.finishTopRunningActivityLocked(app, reason);
9.1 ASS.handleAppCrashLocked

[-> ActivityStackSupervisor.java]

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【9.1.1】
            stacks.get(stackNdx).handleAppCrashLocked(app);
            stackNdx--;
        }
    }
}
9.1.1 AS.handleAppCrashLocked

[-> ActivityStack.java]

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) {
                r.app = null;
                //結束當前activity
                finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
            }
        }
    }
}

這裏的mTaskHistory數據類型爲ArrayList,記錄着所有先前的後臺activities。遍歷所有activities,找到位於該ProcessRecord的所有ActivityRecord,並結束該Acitivity。

9.2 AMS.removeProcessLocked

[-> ActivityManagerService.java]

private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) {
    ...
    if (app.pid > 0 && app.pid != MY_PID) {
        .....
        // 殺進程
        app.kill(reason, true);
         //移除進程並清空該進程相關聯的activity/service等組件
        handleAppDiedLocked(app, willRestart, allowRestart);

        if (willRestart) {
            //此處willRestart=false,不進入該分支
            removeLruProcessLocked(app);
            addAppLocked(app.info, false, null /* ABI override */);
        }
    } else {
        mRemovedProcesses.add(app);
    }
    return needRestart;
}

這裏主要是進程kill以及相關的Activity或者Service等組件清理工作。

9.3 ASS.resumeTopActivitiesLocked

[-> ActivityStackSupervisor.java]

boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target, Bundle targetOptions) {
    if (targetStack == null) {
        targetStack = mFocusedStack;
    }
    boolean result = false;
    if (isFrontStack(targetStack)) {
        result = targetStack.resumeTopActivityLocked(target, targetOptions);
    }

    for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
        final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
            final ActivityStack stack = stacks.get(stackNdx);
            if (stack == targetStack) {
                continue; //已經啓動
            }
            if (isFrontStack(stack)) {
                stack.resumeTopActivityLocked(null);
            }
        }
    }
    return result;
}

被直接kill的進程不需要彈框,則直接啓動下一個activity的ActivityStack,這塊流程在Activity啓動流程中詳細介紹。

執行到這,我們還回過來看 5.crashApplication中,處理完makeAppCrashingLocked,則會再發送消息SHOW_ERROR_UI_MSG,彈出提示crash的對話框,接下來再看看該過程。

10. UiHandler

通過mUiHandler發送message,且消息的msg.waht=ActivityManagerService.SHOW_ERROR_UI_MSG;,接下來進入來看看handleMessage的處理過程。

[-> ActivityManagerService.java]

final class UiHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
                    case SHOW_ERROR_UI_MSG: {
            mAppErrors.handleShowAppErrorUi(msg);
            ensureBootCompleted();
        } break;
        ...
    }
}
10.1 handleShowAppErrorUi

[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.canShowErrorDialogs() || showBackground) && !crashSilenced
                && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
			//創建Crash Dialog
            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();//顯示
    }
}

在發生crash時,默認系統會彈出提示crash的對話框,並阻塞等待用戶選擇是“退出”或 “退出並報告”,當用戶不做任何選擇時5min超時後,默認選擇“退出”,當手機休眠時也默認選擇“退出”。到這裏也並沒有真正結束,在小節2.uncaughtException中在finnally語句塊還有一個殺進程的動作。

11. killProcess

Process.killProcess(Process.myPid());
System.exit(10);

通過finnally語句塊保證能執行並徹底殺掉Crash進程。當Crash進程被殺後,並沒有完全結束,還有Binder死亡通知的流程還沒有處理完成,關於這塊在進程管理中詳細講。

12. 小結

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

app_crash

其中最爲核心的工作圖中紅色部分AMS.handleAppCrashLocked的主要功能:
在這裏插入圖片描述
當同一進程1分鐘之內連續兩次crash,則執行的情況下:

(1) 對於非persistent進程:

  • ASS.handleAppCrashLocked, 直接結束該應用所有activity
  • AMS.removeProcessLocked,殺死該進程以及同一個進程組下的所有進
  • ASS.resumeTopActivitiesLocked,恢復棧頂第一個非finishing狀態的activity

(2)對於persistent進程:

  • ASS.resumeTopActivitiesLocked,恢復棧頂第一個非finishing狀態的activity
    否則,當進程沒連續頻繁crash

  • ASS.finishTopRunningActivityLocked,執行結束棧頂正在運行activity
    另外,AMS.handleAppCrashLocked,該方法內部主要調用鏈,如下:

      AMS.handleAppCrashLocked
         ASS.handleAppCrashLocked
             AS.handleAppCrashLocked
                 AS.finishCurrentActivityLocked
         AMS.removeProcessLocked
             ProcessRecord.kill
             AMS.handleAppDiedLocked
                 ASS.handleAppDiedLocked
                     AMS.cleanUpApplicationRecordLocked
                     AS.handleAppDiedLocked
                         AS.removeHistoryRecordsForAppLocked
      
         ASS.resumeTopActivitiesLocked
             AS.resumeTopActivityLocked
                 AS.resumeTopActivityInnerLocked
         ASS.finishTopRunningActivityLocked
             AS.finishTopRunningActivityLocked
                 AS.finishActivityLocked
    

三、 總結

本文主要以源碼的視角,詳細介紹了到應用crash後系統的處理流程:

首先發生crash所在進程,在創建之初便準備好了defaultUncaughtHandler,用來來處理Uncaught Exception,並輸出當前crash基本信息;

調用當前進程中的AMP.handleApplicationCrash;經過binder ipc機制,傳遞到system_server進程;
接下來,進入system_server進程,調用binder服務端執行AMS.handleApplicationCrash;

從mProcessNames查找到目標進程的ProcessRecord對象;並將進程crash信息輸出到目錄/data/system/dropbox;
執行makeAppCrashingLocked 創建當前用戶下的crash應用的error receiver,並忽略當前應用的廣播;

停止當前進程中所有activity中的WMS的凍結屏幕消息,並執行相關一些屏幕相關操作;

再執行handleAppCrashLocked方法,
當1分鐘內同一進程連續crash兩次時,且非persistent進程,則直接結束該應用所有activity,並殺死該進程以及同一個進程組下的所有進程。然後再恢復棧頂第一個非finishing狀態的activity;

當1分鐘內同一進程連續crash兩次時,且persistent進程,,則只執行恢復棧頂第一個非finishing狀態的activity;

當1分鐘內同一進程未發生連續crash兩次時,則執行結束棧頂正在運行activity的流程。

通過mUiHandler發送消息SHOW_ERROR_UI_MSG,彈出crash對話框;

到此,system_server進程執行完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章