理解Android Crash處理流程

							<blockquote>

基於Android 6.0的源碼剖析, 分析Android應用Crash是如何處理的。

/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java (含內部類AMP)
/frameworks/base/core/java/android/app/ApplicationErrorReport.java

/frameworks/base/services/core/java/com/android/server/ - am/ActivityManagerService.java - am/ProcessRecord.java - am/ActivityRecord.java - am/ActivityStackSupervisor.java - am/ActivityStack.java - am/ActivityRecord.java - am/BroadcastQueue.java - wm/WindowManagerService.java

/libcore/libart/src/main/java/java/lang/Thread.java

一、概述

App crash(全稱Application crash), 對於Crash可分為native crash和framework crash(包含app crash在內),對於crash相信很多app開發者都會遇到,那麼上層什麼時候會出現crash呢,系統又是如何處理crash的呢。例如,在app大家經常使用try...catch語句,那麼如果沒有有效catch exception,就是導致應用crash,發生沒有catch exception,系統便會來進行捕獲,並進入crash流程。如果你是從事Android系統開發或者架構相關工作,或者遇到需要解系統性的疑難雜症,那麼很有必要了解系統Crash處理流程,知其然還需知其所以然;如果你僅僅是App初級開發,可能本文並非很適合閱讀,整個系統流程錯綜複雜。

在Android系統啟動系列文章,已講述過上層應用都是由Zygote fork孵化而來,分為system_server系統進程和各種應用進程,在這些進程創建之初會設置未捕獲異常的處理器,當系統拋出未捕獲的異常時,最終都交給異常處理器。

  • 對於system_server進程:文章Android系統啟動-SystemServer上篇,system_server啟動過程中由RuntimeInit.java的commonInit方法設置UncaughtHandler,用於處理未捕獲異常;
  • 對於普通應用進程:文章理解Android進程創建流程 ,進程創建過程中,同樣會調用RuntimeInit.java的commonInit方法設置UncaughtHandler。

1.1 crash調用鏈

crash流程的方法調用關係來結尾:

AMP.handleApplicationCrash
    AMS.handleApplicationCrash
        AMS.findAppProcess
        AMS.handleApplicationCrashInner
            AMS.addErrorToDropBox
            AMS.crashApplication
                AMS.makeAppCrashingLocked
                    AMS.startAppProblemLocked
                    ProcessRecord.stopFreezingAllLocked
                        ActivityRecord.stopFreezingScreenLocked
                            WMS.stopFreezingScreenLocked
                                WMS.stopFreezingDisplayLocked
                    AMS.handleAppCrashLocked
                mUiHandler.sendMessage(SHOW_ERROR_MSG)

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

接下來說說這個過程。

二、Crash處理流程

那麼接下來以commonInit()方法為起點來展開說明。

1. RuntimeInit.commonInit

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

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

2. UncaughtHandler

[–>RuntimeInit.java]

private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
    //覆寫接口方法
    public void uncaughtException(Thread t, Throwable e) {
        try {
            //保證crash處理過程不會重入
            if (mCrashing) return;
            mCrashing = true;
        <span class="hljs-keyword">if</span> (mApplicationObject == <span class="hljs-keyword">null</span>) {
            <span class="hljs-comment">//system_server進程</span>
            Clog_e(TAG, <span class="hljs-string">"*** FATAL EXCEPTION IN SYSTEM PROCESS: "</span> + t.getName(), e);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">//普通應用進程</span>
            StringBuilder message = <span class="hljs-keyword">new</span> StringBuilder();
            message.append(<span class="hljs-string">"FATAL EXCEPTION: "</span>).append(t.getName()).append(<span class="hljs-string">"\n"</span>);
            <span class="hljs-keyword">final</span> String processName = ActivityThread.currentProcessName();
            <span class="hljs-keyword">if</span> (processName != <span class="hljs-keyword">null</span>) {
                message.append(<span class="hljs-string">"Process: "</span>).append(processName).append(<span class="hljs-string">", "</span>);
            }
            message.append(<span class="hljs-string">"PID: "</span>).append(Process.myPid());
            Clog_e(TAG, message.toString(), e);
        }

        <span class="hljs-comment">//啟動crash對話框,等待處理完成 【見小節2.1和3】</span>
        ActivityManagerNative.getDefault().handleApplicationCrash(
                mApplicationObject, <span class="hljs-keyword">new</span> ApplicationErrorReport.CrashInfo(e));
    } <span class="hljs-keyword">catch</span> (Throwable t2) {
        ...
    } <span class="hljs-keyword">finally</span> {
        <span class="hljs-comment">//確保當前進程徹底殺掉【見小節11】</span>
        Process.killProcess(Process.myPid());
        System.exit(<span class="hljs-number">10</span>);
    }
}

}

  1. 當system進程crash的信息:
    • 開頭*** FATAL EXCEPTION IN SYSTEM PROCESS [線程名]
    • 接著輸出發生crash時的調用棧信息;
  2. 當app進程crash時的信息:
    • 開頭FATAL EXCEPTION: [線程名]
    • 緊接著 Process: [進程名], PID: [進程id]
    • 最後輸出發生crash時的調用棧信息。

看到這裡,你就會發現要從log中搜索crash信息,只需要搜索關鍵詞FATAL EXCEPTION;如果需要進一步篩選只搜索系統crash信息,則可以搜索的關鍵詞可以有多樣,比如*** FATAL EXCEPTION

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

注意: mApplicationObject等於null,一定不是普通的app進程. 但是除了system進程, 也有可能是shell進程, 即通過app_process + 命令參數 的方式創建的進程.

2.1 CrashInfo

[-> ApplicationErrorReport.java]

public class ApplicationErrorReport implements Parcelable {
    ...
    public static class CrashInfo {
        public CrashInfo(Throwable tr) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new FastPrintWriter(sw, false, 256);
            tr.printStackTrace(pw); //輸出棧trace
            pw.flush();
            stackTrace = sw.toString();
            exceptionMessage = tr.getMessage();
        Throwable rootTr = tr;
        <span class="hljs-keyword">while</span> (tr.getCause() != <span class="hljs-keyword">null</span>) {
            tr = tr.getCause();
            <span class="hljs-keyword">if</span> (tr.getStackTrace() != <span class="hljs-keyword">null</span> &amp;&amp; tr.getStackTrace().length &gt; <span class="hljs-number">0</span>) {
                rootTr = tr;
            }
            String msg = tr.getMessage();
            <span class="hljs-keyword">if</span> (msg != <span class="hljs-keyword">null</span> &amp;&amp; msg.length() &gt; <span class="hljs-number">0</span>) {
                exceptionMessage = msg;
            }
        }

        exceptionClassName = rootTr.getClass().getName();
        <span class="hljs-keyword">if</span> (rootTr.getStackTrace().length &gt; <span class="hljs-number">0</span>) {
            StackTraceElement trace = rootTr.getStackTrace()[<span class="hljs-number">0</span>];
            throwFileName = trace.getFileName();
            throwClassName = trace.getClassName();
            throwMethodName = trace.getMethodName();
            throwLineNumber = trace.getLineNumber();
        } <span class="hljs-keyword">else</span> {
            throwFileName = <span class="hljs-string">"unknown"</span>;
            throwClassName = <span class="hljs-string">"unknown"</span>;
            throwMethodName = <span class="hljs-string">"unknown"</span>;
            throwLineNumber = <span class="hljs-number">0</span>;
        }
    }
    ...
}

}

將crash信息文件名類名方法名對應行號以及異常信息都封裝到CrashInfo對象。

3. handleApplicationCrash

[–>ActivityManagerService.java]

public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
    //獲取進程record對象【見小節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):

  1. 當遠程IBinder對象為空時,則進程名為system_server
  2. 當遠程IBinder對象不為空,且ProcessRecord為空時,則進程名為unknown;
  3. 當遠程IBinder對象不為空,且ProcessRecord不為空時,則進程名為ProcessRecord對象中相應進程名。

3.1 findAppProcess

[–>ActivityManagerService.java]

private ProcessRecord findAppProcess(IBinder app, String reason) {
    if (app == null) {
        return null;
    }
<span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) {
    <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> NP = mProcessNames.getMap().size();
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> ip=<span class="hljs-number">0</span>; ip&lt;NP; ip++) {
        SparseArray&lt;ProcessRecord&gt; apps = mProcessNames.getMap().valueAt(ip);
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> NA = apps.size();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> ia=<span class="hljs-number">0</span>; ia&lt;NA; ia++) {
            ProcessRecord p = apps.valueAt(ia);
            <span class="hljs-comment">//當找到目標進程則返回</span>
            <span class="hljs-keyword">if</span> (p.thread != <span class="hljs-keyword">null</span> &amp;&amp; p.thread.asBinder() == app) {
                <span class="hljs-keyword">return</span> p;
            }
        }
    }
    <span class="hljs-comment">//如果代碼執行到這裡,表明無法找到應用所在的進程</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}

}

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

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

再回到mMap,這是以進程name為key,再以(uid為key,以ProcessRecord為Value的)結構體作為value。下面看看其get()和put()方法

//獲取mMap中(name,uid)所對應的ProcessRecord
public ProcessRecord get(String name, int uid) {};
//將(name,uid, value)添加到mMap
public ProcessRecord put(String name, int uid, ProcessRecord value) {};

findAppProcess()根據app(IBinder類型)來查詢相應的目標對象ProcessRecord。

有了進程記錄對象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,...);
    //將錯誤信息添加到DropBox
    addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
    //【見小節5】
    crashApplication(r, crashInfo);
}

其中addErrorToDropBox是將crash的信息輸出到目錄/data/system/dropbox。例如system_server的dropbox文件名為system_server_crash@xxx.txt (xxx代表的是時間戳)

5. crashApplication

[–>ActivityManagerService.java]

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 = <span class="hljs-keyword">new</span> AppErrorResult();
<span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) {
    <span class="hljs-comment">// 當存在ActivityController的情況,比如monkey</span>
    <span class="hljs-keyword">if</span> (mController != <span class="hljs-keyword">null</span>) {
        <span class="hljs-keyword">try</span> {
            String name = r != <span class="hljs-keyword">null</span> ? r.processName : <span class="hljs-keyword">null</span>;
            <span class="hljs-keyword">int</span> pid = r != <span class="hljs-keyword">null</span> ? r.pid : Binder.getCallingPid();
            <span class="hljs-keyword">int</span> uid = r != <span class="hljs-keyword">null</span> ? r.info.uid : Binder.getCallingUid();
            <span class="hljs-comment">//調用monkey的appCrashed</span>
            <span class="hljs-keyword">if</span> (!mController.appCrashed(name, pid,
                    shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
                <span class="hljs-keyword">if</span> (<span class="hljs-string">"1"</span>.equals(SystemProperties.get(SYSTEM_DEBUGGABLE, <span class="hljs-string">"0"</span>))
                        &amp;&amp; <span class="hljs-string">"Native crash"</span>.equals(crashInfo.exceptionClassName)) {
                    Slog.w(TAG, <span class="hljs-string">"Skip killing native crashed app "</span> + name
                            + <span class="hljs-string">"("</span> + pid + <span class="hljs-string">") during testing"</span>);
                } <span class="hljs-keyword">else</span> {
                    Slog.w(TAG, <span class="hljs-string">"Force-killing crashed app "</span> + name
                            + <span class="hljs-string">" at watcher's request"</span>);
                    <span class="hljs-keyword">if</span> (r != <span class="hljs-keyword">null</span>) {
                        r.kill(<span class="hljs-string">"crash"</span>, <span class="hljs-keyword">true</span>);
                    } <span class="hljs-keyword">else</span> {
                        Process.killProcess(pid);
                        killProcessGroup(uid, pid);
                    }
                }
                <span class="hljs-keyword">return</span>;
            }
        } <span class="hljs-keyword">catch</span> (RemoteException e) {
            mController = <span class="hljs-keyword">null</span>;
            Watchdog.getInstance().setActivityController(<span class="hljs-keyword">null</span>);
        }
    }
    
    <span class="hljs-comment">//清除遠程調用者uid和pid信息,並保存到origId</span>
    <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> origId = Binder.clearCallingIdentity();
    ...

    <span class="hljs-comment">//【見小節6】</span>
    <span class="hljs-keyword">if</span> (r == <span class="hljs-keyword">null</span> || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {
        Binder.restoreCallingIdentity(origId);
        <span class="hljs-keyword">return</span>;
    }

    Message msg = Message.obtain();
    msg.what = SHOW_ERROR_MSG;
    HashMap data = <span class="hljs-keyword">new</span> HashMap();
    data.put(<span class="hljs-string">"result"</span>, result);
    data.put(<span class="hljs-string">"app"</span>, r);
    msg.obj = data;
    <span class="hljs-comment">//發送消息SHOW_ERROR_MSG,彈出提示crash的對話框,等待用戶選擇【見小節10】</span>
    mUiHandler.sendMessage(msg);
    <span class="hljs-comment">//恢復遠程調用者uid和pid</span>
    Binder.restoreCallingIdentity(origId);
}

<span class="hljs-comment">//進入阻塞等待,直到用戶選擇crash對話框"退出"或者"退出並報告"</span>
<span class="hljs-keyword">int</span> res = result.get();

Intent appErrorIntent = <span class="hljs-keyword">null</span>;
<span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) {
    <span class="hljs-keyword">if</span> (r != <span class="hljs-keyword">null</span> &amp;&amp; !r.isolated) {
        <span class="hljs-comment">// 將崩潰的進程信息保存到mProcessCrashTimes</span>
        mProcessCrashTimes.put(r.info.processName, r.uid,
                SystemClock.uptimeMillis());
    }
    <span class="hljs-keyword">if</span> (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
        <span class="hljs-comment">//創建action="android.intent.action.APP_ERROR",組件為r.errorReportReceiver的Intent</span>
        appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
    }
}

<span class="hljs-keyword">if</span> (appErrorIntent != <span class="hljs-keyword">null</span>) {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">//啟動Intent為appErrorIntent的Activity</span>
        mContext.startActivityAsUser(appErrorIntent, <span class="hljs-keyword">new</span> UserHandle(r.userId));
    } <span class="hljs-keyword">catch</span> (ActivityNotFoundException e) {
        Slog.w(TAG, <span class="hljs-string">"bug report receiver dissappeared"</span>, e);
    }
}

}

該方法主要做的兩件事:

  • 調用makeAppCrashingLocked,繼續處理crash流程;
  • 發送消息SHOW_ERROR_MSG,彈出提示crash的對話框,等待用戶選擇;

6. makeAppCrashingLocked

[–>ActivityManagerService.java]

private boolean makeAppCrashingLocked(ProcessRecord app,
        String shortMsg, String longMsg, String stackTrace) {
    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);
}

7. startAppProblemLocked

[–>ActivityManagerService.java]

void startAppProblemLocked(ProcessRecord app) {
    app.errorReportReceiver = null;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> userId : mCurrentProfileIds) {
    <span class="hljs-keyword">if</span> (app.userId == userId) {
        <span class="hljs-comment">//獲取當前用戶下的crash應用的error receiver【見小節7.1】</span>
        app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
                mContext, app.info.packageName, app.info.flags);
    }
}
<span class="hljs-comment">//忽略當前app的廣播接收【見小節7.2】</span>
skipCurrentReceiverLocked(app);

}

該方法主要功能:

  • 獲取當前用戶下的crash應用的error receiver;
  • 忽略當前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 = <span class="hljs-literal">null</span>;
ComponentName result = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">try</span> {
    <span class="hljs-comment">//獲取該crash應用的安裝器的包名</span>
    candidate = pm.getInstallerPackageName(packageName);
} <span class="hljs-keyword">catch</span> (IllegalArgumentException e) {
}

<span class="hljs-keyword">if</span> (candidate != <span class="hljs-literal">null</span>) {
    result = getErrorReportReceiver(pm, packageName, candidate);<span class="hljs-comment">//【見下文】</span>
    <span class="hljs-keyword">if</span> (result != <span class="hljs-literal">null</span>) {
        <span class="hljs-comment">//2.當找到該crash應用的安裝器,則返回;</span>
        <span class="hljs-keyword">return</span> result;
    }
}

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

<span class="hljs-comment">//該默認屬性名為"ro.error.receiver.default"</span>
candidate = SystemProperties.<span class="hljs-keyword">get</span>(DEFAULT_ERROR_RECEIVER_PROPERTY);
<span class="hljs-comment">//4.當默認屬性值指定error receiver時,則返回;</span>
<span class="hljs-keyword">return</span> getErrorReportReceiver(pm, packageName, candidate); <span class="hljs-comment">//【見下文】</span>

}

getErrorReportReceiver:這是同名不同輸入參數的另一個方法:

static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
        String receiverPackage) {
    if (receiverPackage == null || receiverPackage.length() == 0) {
        return null;
    }
<span class="hljs-comment">//當安裝應用程序的安裝器Crash,則直接返回</span>
<span class="hljs-keyword">if</span> (receiverPackage.equals(errorPackage)) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}

<span class="hljs-comment">//ACTION_APP_ERROR值為"android.intent.action.APP_ERROR"</span>
Intent intent = <span class="hljs-keyword">new</span> Intent(Intent.ACTION_APP_ERROR);
intent.setPackage(receiverPackage);
ResolveInfo info = pm.resolveActivity(intent, <span class="hljs-number">0</span>);
<span class="hljs-keyword">if</span> (info == <span class="hljs-literal">null</span> || info.activityInfo == <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}
<span class="hljs-comment">//創建包名為receiverPackage的組件</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ComponentName(receiverPackage, info.activityInfo.name);

}

7.2 skipCurrentReceiverLocked

[–>ActivityManagerService.java]

void skipCurrentReceiverLocked(ProcessRecord app) {
    for (BroadcastQueue queue : mBroadcastQueues) {
        queue.skipCurrentReceiverLocked(app);  //【見小節7.2.1】
    }
}
7.2.1 skipCurrentReceiverLocked

[-> BroadcastQueue.java]

public void skipCurrentReceiverLocked(ProcessRecord app) {
    BroadcastRecord r = null;
    //查看app進程中的廣播
    if (mOrderedBroadcasts.size() > 0) {
        BroadcastRecord br = mOrderedBroadcasts.get(0);
        if (br.curApp == app) {
            r = br;
        }
    }
    if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
        r = mPendingBroadcast;
    }
<span class="hljs-keyword">if</span> (r != <span class="hljs-literal">null</span>) {
    <span class="hljs-comment">//結束app進程的廣播結束</span>
    finishReceiverLocked(r, r.resultCode, r.resultData,
            r.resultExtras, r.resultAbort, <span class="hljs-literal">false</span>);
    <span class="hljs-comment">//廣播調度</span>
    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<ActivityRecord>,停止進程裡所有的Activity

8.1. AR.stopFreezingScreenLocked

[-> ActivityRecord.java]

public void stopFreezingScreenLocked(boolean force) {
    if (force || frozenBeforeDestroy) {
        frozenBeforeDestroy = false;
        //mWindowManager類型為WMS //【見小節8.1.1】
        service.mWindowManager.stopAppFreezingScreen(appToken, force);
    }
}

其中appToken是IApplication.Stub類型,即WindowManager的token。

8.1.1 WMS.stopFreezingScreenLocked

[-> WindowManagerService.java]

@Override
public void stopFreezingScreen() {
    //權限檢查
    if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
            "stopFreezingScreen()")) {
        throw new SecurityException("Requires FREEZE_SCREEN permission");
    }
<span class="hljs-keyword">synchronized</span>(mWindowMap) {
    <span class="hljs-keyword">if</span> (mClientFreezingScreen) {
        mClientFreezingScreen = <span class="hljs-keyword">false</span>;
        mLastFinishedFreezeSource = <span class="hljs-string">"client"</span>;
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> origId = Binder.clearCallingIdentity();
        <span class="hljs-keyword">try</span> {
            stopFreezingDisplayLocked(); <span class="hljs-comment">//【見流程8.1.1.1】</span>
        } <span class="hljs-keyword">finally</span> {
            Binder.restoreCallingIdentity(origId);
        }
    }
}

}

8.1.1.1 WMS.stopFreezingDisplayLocked

[-> WindowManagerService.java]

private void stopFreezingDisplayLocked() {
    if (!mDisplayFrozen) {
        return; //顯示沒有凍結,則直接返回
    }
<span class="hljs-comment">//往往跟屏幕旋轉相關</span>
...

mDisplayFrozen = <span class="hljs-keyword">false</span>;
<span class="hljs-comment">//從上次凍屏到現在的總時長</span>
mLastDisplayFreezeDuration = (<span class="hljs-keyword">int</span>)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);

<span class="hljs-comment">//移除凍屏的超時消息</span>
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);

<span class="hljs-keyword">boolean</span> updateRotation = <span class="hljs-keyword">false</span>;
<span class="hljs-comment">//獲取默認的DisplayContent</span>
<span class="hljs-keyword">final</span> DisplayContent displayContent = getDefaultDisplayContentLocked();
<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> displayId = displayContent.getDisplayId();
ScreenRotationAnimation screenRotationAnimation =
        mAnimator.getScreenRotationAnimationLocked(displayId);

<span class="hljs-comment">//屏幕旋轉動畫的相關操作</span>
<span class="hljs-keyword">if</span> (CUSTOM_SCREEN_ROTATION &amp;&amp; screenRotationAnimation != <span class="hljs-keyword">null</span>
        &amp;&amp; screenRotationAnimation.hasScreenshot()) {
    DisplayInfo displayInfo = displayContent.getDisplayInfo();
    <span class="hljs-keyword">boolean</span> isDimming = displayContent.isDimming();
    <span class="hljs-keyword">if</span> (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
        mExitAnimId = mEnterAnimId = <span class="hljs-number">0</span>;
    }
    <span class="hljs-comment">//加載動畫最大時長為10s</span>
    <span class="hljs-keyword">if</span> (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
            getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
        scheduleAnimationLocked();
    } <span class="hljs-keyword">else</span> {
        screenRotationAnimation.kill();
        mAnimator.setScreenRotationAnimationLocked(displayId, <span class="hljs-keyword">null</span>);
        updateRotation = <span class="hljs-keyword">true</span>;
    }
} <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">if</span> (screenRotationAnimation != <span class="hljs-keyword">null</span>) {
        screenRotationAnimation.kill();
        mAnimator.setScreenRotationAnimationLocked(displayId, <span class="hljs-keyword">null</span>);
    }
    updateRotation = <span class="hljs-keyword">true</span>;
}
<span class="hljs-comment">//經過層層調用到InputManagerService服務,IMS服務使能輸入事件分發功能</span>
mInputMonitor.thawInputDispatchingLw();

<span class="hljs-keyword">boolean</span> configChanged;
<span class="hljs-comment">//當display被凍結時不再計算屏幕方向,以避免不連續的狀態。</span>
configChanged = updateOrientationFromAppTokensLocked(<span class="hljs-keyword">false</span>);

<span class="hljs-comment">//display凍結時,執行gc操作</span>
mH.removeMessages(H.FORCE_GC);
mH.sendEmptyMessageDelayed(H.FORCE_GC, <span class="hljs-number">2000</span>);

<span class="hljs-comment">//mScreenFrozenLock的類型為PowerManager.WakeLock,即釋放屏幕凍結的鎖</span>
mScreenFrozenLock.release();

<span class="hljs-keyword">if</span> (updateRotation) {
    <span class="hljs-comment">//更新當前的屏幕方向</span>
    configChanged |= updateRotationUncheckedLocked(<span class="hljs-keyword">false</span>);
}

<span class="hljs-keyword">if</span> (configChanged) {
    <span class="hljs-comment">//向mH發送configuraion改變的消息</span>
    mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}

}

該方法主要功能:

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

9.AMS.handleAppCrashLocked

[-> ActivityManagerService.java]

private boolean handleAppCrashLocked(ProcessRecord app, String reason,
        String shortMsg, String longMsg, String stackTrace) {
    long now = SystemClock.uptimeMillis();
Long crashTime;
<span class="hljs-keyword">if</span> (!app.isolated) {
    crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
} <span class="hljs-keyword">else</span> {
    crashTime = <span class="hljs-keyword">null</span>;
}
<span class="hljs-comment">//當同一個進程,連續兩次crash的時間間隔小於1分鐘時,則認為crash太過於頻繁</span>
<span class="hljs-keyword">if</span> (crashTime != <span class="hljs-keyword">null</span> &amp;&amp; now &lt; crashTime+ProcessList.MIN_CRASH_INTERVAL) {
    EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
            app.userId, app.info.processName, app.uid);
    <span class="hljs-comment">//【見小節9.1】</span>
    mStackSupervisor.handleAppCrashLocked(app);
    <span class="hljs-keyword">if</span> (!app.persistent) {
        <span class="hljs-comment">//不再重啟非persistent進程,除非用戶顯式地調用</span>
        EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
                app.info.processName);
        <span class="hljs-keyword">if</span> (!app.isolated) {
            <span class="hljs-comment">//將當前app加入到mBadProcesses</span>
            mBadProcesses.put(app.info.processName, app.uid,
                    <span class="hljs-keyword">new</span> BadProcessInfo(now, shortMsg, longMsg, stackTrace));
            mProcessCrashTimes.remove(app.info.processName, app.uid);
        }
        app.bad = <span class="hljs-keyword">true</span>;
        app.removed = <span class="hljs-keyword">true</span>;
        <span class="hljs-comment">//移除進程的所有服務,保證不再重啟【見小節9.2】</span>
        removeProcessLocked(app, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-string">"crash"</span>);
        <span class="hljs-comment">//恢復最頂部的Activity【見小節9.3】</span>
        mStackSupervisor.resumeTopActivitiesLocked();
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
    mStackSupervisor.resumeTopActivitiesLocked();
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">//此處reason="force-crash"【見小節9.4】</span>
    mStackSupervisor.finishTopRunningActivityLocked(app, reason);
}

<span class="hljs-comment">//運行在當前進程中的所有服務的crash次數執行加1操作</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i=app.services.size()-<span class="hljs-number">1</span>; i&gt;=<span class="hljs-number">0</span>; i--) {
    ServiceRecord sr = app.services.valueAt(i);
    sr.crashCount++;
}

<span class="hljs-comment">//當桌面應用crash,並且被三方app所取代,那麼需要清空桌面應用的偏愛選項。</span>
<span class="hljs-keyword">final</span> ArrayList&lt;ActivityRecord&gt; activities = app.activities;
<span class="hljs-keyword">if</span> (app == mHomeProcess &amp;&amp; activities.size() &gt; <span class="hljs-number">0</span>
            &amp;&amp; (mHomeProcess.info.flags &amp; ApplicationInfo.FLAG_SYSTEM) == <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> activityNdx = activities.size() - <span class="hljs-number">1</span>; activityNdx &gt;= <span class="hljs-number">0</span>; --activityNdx) {
        <span class="hljs-keyword">final</span> ActivityRecord r = activities.get(activityNdx);
        <span class="hljs-keyword">if</span> (r.isHomeActivity()) {
            <span class="hljs-comment">//清空偏愛應用</span>
            ActivityThread.getPackageManager()
                    .clearPackagePreferredActivities(r.packageName);
        }
    }
}

<span class="hljs-keyword">if</span> (!app.isolated) {
    <span class="hljs-comment">//無法記錄孤立進程的crash時間點,由於他們並沒有一個固定身份</span>
    mProcessCrashTimes.put(app.info.processName, app.uid, now);
}
<span class="hljs-comment">//當app存在crash的handler,那麼交給其處理</span>
<span class="hljs-keyword">if</span> (app.crashHandler != <span class="hljs-keyword">null</span>) mHandler.post(app.crashHandler);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;

}

  1. 當同一進程在時間間隔小於1分鐘時連續兩次crash,則執行的情況下:
    • 對於非persistent進程:
      • [9.1] mStackSupervisor.handleAppCrashLocked(app);
      • [9.2] removeProcessLocked(app, false, false, “crash”);
      • [9.3] mStackSupervisor.resumeTopActivitiesLocked();
    • 對於persistent進程,則只執行
      • [9.3] mStackSupervisor.resumeTopActivitiesLocked();
  2. 否則執行
    • [9.4] 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) {
    final String name = app.processName;
    final int uid = app.uid;
    //從mProcessNames移除該進程
    removeProcessNameLocked(name, uid);
    ...
    if (app.pid > 0 && app.pid != MY_PID) {
        int pid = app.pid;
        synchronized (mPidsSelfLocked) {
            mPidsSelfLocked.remove(pid); //移除該pid
            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
        }
        ...
        boolean willRestart = false;
        //對於非孤立的persistent進程設置成可重啟flags
        if (app.persistent && !app.isolated) {
            if (!callerWillRestart) {
                willRestart = true;
            } else {
                needRestart = true;
            }
        }
        // 殺進程【9.2.1】
        app.kill(reason, true);
         //移除進程並清空該進程相關聯的activity/service等組件 【9.2.2】
        handleAppDiedLocked(app, willRestart, allowRestart);
    <span class="hljs-keyword">if</span> (willRestart) {
        <span class="hljs-comment">//此處willRestart=false,不進入該分支</span>
        removeLruProcessLocked(app);
        addAppLocked(app.info, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">null</span> <span class="hljs-comment">/* ABI override */</span>);
    }
} <span class="hljs-keyword">else</span> {
    mRemovedProcesses.add(app);
}
<span class="hljs-keyword">return</span> needRestart;

}

  • mProcessNames數據類型為ProcessMap ,這是以進程名為key,記錄著所有的ProcessRecord信息
  • mPidsSelfLocked數據類型為SparseArray ,這是以pid為key,記錄著所有的ProcessRecord信息。該對象的同步保護是通過自身鎖,而非全局ActivityManager鎖。
9.2.1 app.kill

[-> ProcessRecord.java]

void kill(String reason, boolean noisy) {
    if (!killedByAm) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
        if (noisy) {
            Slog.i(TAG, "Killing " + toShortString() + " (adj " + setAdj + "): " + reason);
        }
        EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
        Process.killProcessQuiet(pid); //殺進程
        Process.killProcessGroup(info.uid, pid); //殺進程組,包括native進程
        if (!persistent) {
            killed = true;
            killedByAm = true;
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }
}

此處reason為“crash”,關於殺進程的過程見我的另一篇文章理解殺進程的實現原理.

9.2.2 handleAppDiedLocked

[-> ActivityManagerService.java]

private final void handleAppDiedLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart) {
    int pid = app.pid;
    //清除應用中service/receiver/ContentProvider信息
    boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
    if (!kept && !restarting) {
        removeLruProcessLocked(app);
        if (pid > 0) {
            ProcessList.remove(pid);
        }
    }
<span class="hljs-keyword">if</span> (mProfileProc == app) {
    clearProfilerLocked();
}

<span class="hljs-comment">//清除應用中activity相關信息</span>
<span class="hljs-keyword">boolean</span> hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);

app.activities.clear();
...
<span class="hljs-keyword">if</span> (!restarting &amp;&amp; hasVisibleActivities &amp;&amp; !mStackSupervisor.resumeTopActivitiesLocked()) {
    mStackSupervisor.ensureActivitiesVisibleLocked(<span class="hljs-keyword">null</span>, <span class="hljs-number">0</span>);
}

}

9.3 ASS.resumeTopActivitiesLocked

[-> ActivityStackSupervisor.java]

boolean resumeTopActivitiesLocked() {
    return resumeTopActivitiesLocked(null, null, null);
}

boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target, Bundle targetOptions) { if (targetStack == null) { targetStack = mFocusedStack; } boolean result = false; if (isFrontStack(targetStack)) { //【見小節9.3.1】 result = targetStack.resumeTopActivityLocked(target, targetOptions); }

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> displayNdx = mActivityDisplays.size() - <span class="hljs-number">1</span>; displayNdx &gt;= <span class="hljs-number">0</span>; --displayNdx) {
    <span class="hljs-keyword">final</span> ArrayList&lt;ActivityStack&gt; stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> stackNdx = stacks.size() - <span class="hljs-number">1</span>; stackNdx &gt;= <span class="hljs-number">0</span>; --stackNdx) {
        <span class="hljs-keyword">final</span> ActivityStack stack = stacks.get(stackNdx);
        <span class="hljs-keyword">if</span> (stack == targetStack) {
            <span class="hljs-keyword">continue</span>; <span class="hljs-comment">//已經啟動</span>
        }
        <span class="hljs-keyword">if</span> (isFrontStack(stack)) {
            stack.resumeTopActivityLocked(<span class="hljs-keyword">null</span>);
        }
    }
}
<span class="hljs-keyword">return</span> result;

}

此處mFocusedStack是當前正在等待接收input事件或者正在啟動下一個activity的ActivityStack

9.3.1 AS.resumeTopActivityLocked

[-> ActivityStack.java]

final boolean .resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
    ...
    result = resumeTopActivityInnerLocked(prev, options);//【見小節9.3.2】
    return result;
}
9.3.2 AS.resumeTopActivityInnerLocked

[-> ActivityStack.java]

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
    //找到mTaskHistory棧中第一個未處於finishing狀態的Activity
    final ActivityRecord next = topRunningActivityLocked(null);
<span class="hljs-keyword">if</span> (mResumedActivity == next &amp;&amp; next.state == ActivityState.RESUMED &amp;&amp;
                    mStackSupervisor.allResumedActivitiesComplete()) {
    <span class="hljs-comment">//當top activity已經處於resume,則無需操作;</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
}

<span class="hljs-keyword">if</span> (mService.isSleepingOrShuttingDown()
        &amp;&amp; mLastPausedActivity == next
        &amp;&amp; mStackSupervisor.allPausedActivitiesComplete()) {
    <span class="hljs-comment">//當正處於sleeping狀態,top activity處於paused,則無需操作</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
}

<span class="hljs-comment">//正在啟動app的activity,確保app不會被設置為stopped</span>
AppGlobals.getPackageManager().setPackageStoppedState(
            next.packageName, <span class="hljs-keyword">false</span>, next.userId);
<span class="hljs-comment">//回調應用onResume方法</span>
next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                        mService.isNextTransitionForward(), resumeAnimOptions);
...

}

該方法代碼比較長,這裡就簡單列舉幾條比較重要的代碼。執行完該方法,應用也便完成了activity的resume過程。

9.4 finishTopRunningActivityLocked

9.4.1 ASS.finishTopRunningActivityLocked

[-> ActivityStackSupervisor.java]

void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
    for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
        final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
        final int numStacks = stacks.size();
        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
            final ActivityStack stack = stacks.get(stackNdx);
            //此處reason= "force-crash"【見小節9.4.2】
            stack.finishTopRunningActivityLocked(app, reason);
        }
    }
}
9.4.2 AS.finishTopRunningActivityLocked
final void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
    //找到棧頂第一個不處於finishing狀態的activity
    ActivityRecord r = topRunningActivityLocked(null);
    if (r != null && r.app == app) {
        int taskNdx = mTaskHistory.indexOf(r.task);
        int activityNdx = r.task.mActivities.indexOf(r);
        //【見小節9.4.3】
        finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
        --activityNdx;
        if (activityNdx < 0) {
            do {
                --taskNdx;
                if (taskNdx < 0) {
                    break;
                }
                activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
            } while (activityNdx < 0);
        }
        if (activityNdx >= 0) {
            r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
            if (r.state == ActivityState.RESUMED
                    || r.state == ActivityState.PAUSING
                    || r.state == ActivityState.PAUSED) {
                if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
                    //【見小節9.4.3】
                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
                }
            }
        }
    }
}
9.4.3 AS.finishActivityLocked
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
           String reason, boolean oomAdj) {
    if (r.finishing) {
        return false; //正在finishing則返回
    }
    //設置finish狀態的activity不可見
    r.makeFinishingLocked();
    //暫停key的分發事件
    r.pauseKeyDispatchingLocked();
mWindowManager.prepareAppTransition(endTask
        ? AppTransition.TRANSIT_TASK_CLOSE
        : AppTransition.TRANSIT_ACTIVITY_CLOSE, <span class="hljs-keyword">false</span>);

mWindowManager.setAppVisibility(r.appToken, <span class="hljs-keyword">false</span>);
<span class="hljs-comment">//回調activity的onPause方法</span>
startPausingLocked(<span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>);
...

}

該方法最終會回調到activity的pause方法。

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

10. UiHandler

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

[-> ActivityManagerService.java]

final class UiHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case SHOW_ERROR_MSG: {
           HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
           synchronized (ActivityManagerService.this) {
               ProcessRecord proc = (ProcessRecord)data.get("app");
               AppErrorResult res = (AppErrorResult) data.get("result");
              、
               boolean isBackground = (UserHandle.getAppId(proc.uid)
                       >= Process.FIRST_APPLICATION_UID
                       && proc.pid != MY_PID);
                ...
           <span class="hljs-keyword">if</span> (mShowDialogs &amp;&amp; !mSleeping &amp;&amp; !mShuttingDown) {
               <span class="hljs-comment">//創建提示crash對話框,等待用戶選擇,5分鐘操作等待。</span>
               Dialog d = <span class="hljs-keyword">new</span> AppErrorDialog(mContext,
                       ActivityManagerService.<span class="hljs-keyword">this</span>, res, proc);
               d.show();
               proc.crashDialog = d;
           } <span class="hljs-keyword">else</span> {
               <span class="hljs-comment">//當處於sleep狀態,則默認選擇退出。</span>
               <span class="hljs-keyword">if</span> (res != <span class="hljs-keyword">null</span>) {
                   res.set(<span class="hljs-number">0</span>);
               }
           }
       }
    } <span class="hljs-keyword">break</span>;
    ...
}

}

在發生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. 當同一進程1分鐘之內連續兩次crash,則執行的情況下:
    • 對於非persistent進程:
      • ASS.handleAppCrashLocked, 直接結束該應用所有activity
      • AMS.removeProcessLocked,殺死該進程以及同一個進程組下的所有進
      • ASS.resumeTopActivitiesLocked,恢復棧頂第一個非finishing狀態的activity
    • 對於persistent進程,則只執行
      • ASS.resumeTopActivitiesLocked,恢復棧頂第一個非finishing狀態的activity
  2. 否則,當進程沒連續頻繁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

三、Binder死亡通知

進程被殺,如果還記得Binder的死亡回調機制,在應用進程創建的過程中有一個attachApplicationLocked方法的過程中便會創建死亡通知。

[-> ActivityManagerService.java]

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
    try {
        //創建binder死亡通知
        AppDeathRecipient adr = new AppDeathRecipient(
                app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
        app.deathRecipient = adr;
    } catch (RemoteException e) {
        app.resetPackageList(mProcessStats);
        startProcessLocked(app, "link fail", processName);
        return false;
    }
    ...
}

當binder服務端掛了之後,便會通過binder的DeathRecipient來通知AMS進行相應的清理收尾工作。前面已經降到crash的進程會被kill掉,那麼當該進程會殺,則會回調到binderDied()方法。

1. binderDied

[-> ActivityManagerService.java]

private final class AppDeathRecipient implements IBinder.DeathRecipient {
    public void binderDied() {
        synchronized(ActivityManagerService.this) {
            appDiedLocked(mApp, mPid, mAppThread, true);//【見小節2】
        }
    }
}

2. appDiedLocked

final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
        boolean fromBinderDied) {
    ...
    if (!app.killed) {
        if (!fromBinderDied) {
            Process.killProcessQuiet(pid);
        }
        killProcessGroup(app.info.uid, pid);
        app.killed = true;
    }
<span class="hljs-comment">// Clean up already done if the process has been re-started.</span>
<span class="hljs-keyword">if</span> (app.pid == pid &amp;&amp; app.thread != <span class="hljs-keyword">null</span> &amp;&amp;
        app.thread.asBinder() == thread.asBinder()) {
    <span class="hljs-keyword">boolean</span> doLowMem = app.instrumentationClass == <span class="hljs-keyword">null</span>;
    <span class="hljs-keyword">boolean</span> doOomAdj = doLowMem;
    <span class="hljs-keyword">if</span> (!app.killedByAm) {
        mAllowLowerMemLevel = <span class="hljs-keyword">true</span>;
    } <span class="hljs-keyword">else</span> {
        mAllowLowerMemLevel = <span class="hljs-keyword">false</span>;
        doLowMem = <span class="hljs-keyword">false</span>;
    }
    <span class="hljs-comment">//【見小節3】</span>
    handleAppDiedLocked(app, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">true</span>);

    <span class="hljs-keyword">if</span> (doOomAdj) {
        updateOomAdjLocked();
    }
    <span class="hljs-keyword">if</span> (doLowMem) {
        doLowMemReportIfNeededLocked(app);
    }
}
...

}

3 handleAppDiedLocked

[-> ActivityManagerService.java]

private final void handleAppDiedLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart) {
    int pid = app.pid;
    //清理應用程序service, BroadcastReceiver, ContentProvider相關信息【見小節4】
    boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
<span class="hljs-keyword">if</span> (!kept &amp;&amp; !restarting) {
    removeLruProcessLocked(app);
    <span class="hljs-keyword">if</span> (pid &gt; <span class="hljs-number">0</span>) {
        ProcessList.remove(pid);
    }
}

<span class="hljs-comment">//清理activity相關信息</span>
<span class="hljs-keyword">boolean</span> hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
app.activities.clear();
...
<span class="hljs-comment">//恢復棧頂第一個非finish的activity</span>
<span class="hljs-keyword">if</span> (!restarting &amp;&amp; hasVisibleActivities &amp;&amp; !mStackSupervisor.resumeTopActivitiesLocked()) {
   mStackSupervisor.ensureActivitiesVisibleLocked(<span class="hljs-keyword">null</span>, <span class="hljs-number">0</span>);

} }

4 cleanUpApplicationRecordLocked

該方法清理應用程序service, BroadcastReceiver, ContentProvider,process相關信息,為了便於說明將該方法劃分為4個部分講解

4.1 清理service

參數restarting = false, allowRestart =true, index =-1

private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart, int index) {
    ...
    mProcessesToGc.remove(app);
    mPendingPssProcesses.remove(app);
<span class="hljs-comment">//如果存在,則清除crash/anr/wait對話框</span>
<span class="hljs-keyword">if</span> (app.crashDialog != <span class="hljs-keyword">null</span> &amp;&amp; !app.forceCrashReport) {
    app.crashDialog.dismiss();
    app.crashDialog = <span class="hljs-keyword">null</span>;
}
<span class="hljs-keyword">if</span> (app.anrDialog != <span class="hljs-keyword">null</span>) {
    app.anrDialog.dismiss();
    app.anrDialog = <span class="hljs-keyword">null</span>;
}
<span class="hljs-keyword">if</span> (app.waitDialog != <span class="hljs-keyword">null</span>) {
    app.waitDialog.dismiss();
    app.waitDialog = <span class="hljs-keyword">null</span>;
}

app.crashing = <span class="hljs-keyword">false</span>;
app.notResponding = <span class="hljs-keyword">false</span>;

app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient(); <span class="hljs-comment">//解除app的死亡通告</span>
app.makeInactive(mProcessStats);
app.waitingToKill = <span class="hljs-keyword">null</span>;
app.forcingToForeground = <span class="hljs-keyword">null</span>;
<span class="hljs-comment">//將app移除前臺進程</span>
updateProcessForegroundLocked(app, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>);
app.foregroundActivities = <span class="hljs-keyword">false</span>;
app.hasShownUi = <span class="hljs-keyword">false</span>;
app.treatLikeActivity = <span class="hljs-keyword">false</span>;
app.hasAboveClient = <span class="hljs-keyword">false</span>;
app.hasClientActivities = <span class="hljs-keyword">false</span>;
<span class="hljs-comment">//清理service信息,這個過程也比較複雜,後續再展開</span>
mServices.killServicesLocked(app, allowRestart);
<span class="hljs-keyword">boolean</span> restart = <span class="hljs-keyword">false</span>;

}

  • mProcessesToGc:記錄著需要儘快執行gc的進程列表
  • mPendingPssProcesses:記錄著需要收集內存信息的進程列表

4.2 清理ContentProvider

private final boolean cleanUpApplicationRecordLocked(...) {
    ...
    for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
        //獲取該進程已發表的ContentProvider
        ContentProviderRecord cpr = app.pubProviders.valueAt(i);
        final boolean always = app.bad || !allowRestart;
        //ContentProvider服務端被殺,則client端進程也會被殺
        boolean inLaunching = removeDyingProviderLocked(app, cpr, always);
        if ((inLaunching || always) && cpr.hasConnectionOrHandle()) {
            restart = true; //需要重啟
        }
    cpr.provider = <span class="hljs-keyword">null</span>;
    cpr.proc = <span class="hljs-keyword">null</span>;
}
app.pubProviders.clear();

<span class="hljs-comment">//處理正在啟動並且是有client端正在等待的ContentProvider</span>
<span class="hljs-keyword">if</span> (cleanupAppInLaunchingProvidersLocked(app, <span class="hljs-keyword">false</span>)) {
    restart = <span class="hljs-keyword">true</span>;
}

<span class="hljs-comment">//取消已連接的ContentProvider的註冊</span>
<span class="hljs-keyword">if</span> (!app.conProviders.isEmpty()) {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = app.conProviders.size() - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i--) {
        ContentProviderConnection conn = app.conProviders.get(i);
        conn.provider.connections.remove(conn);

        stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
                conn.provider.name);
    }
    app.conProviders.clear();
}

4.3 清理BroadcastReceiver

private final boolean cleanUpApplicationRecordLocked(...) {
    ...
    skipCurrentReceiverLocked(app);
<span class="hljs-comment">// 取消註冊的廣播接收者</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = app.receivers.size() - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i--) {
    removeReceiverLocked(app.receivers.valueAt(i));
}
app.receivers.clear();

}

4.4 清理Process

private final boolean cleanUpApplicationRecordLocked(...) {
    ...
    //當app正在備份時的處理方式
    if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
        ...
        IBackupManager bm = IBackupManager.Stub.asInterface(
                ServiceManager.getService(Context.BACKUP_SERVICE));
        bm.agentDisconnected(app.info.packageName);
    }
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = mPendingProcessChanges.size() - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i--) {
    ProcessChangeItem item = mPendingProcessChanges.get(i);
    <span class="hljs-keyword">if</span> (item.pid == app.pid) {
        mPendingProcessChanges.remove(i);
        mAvailProcessChanges.add(item);
    }
}
mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, <span class="hljs-keyword">null</span>).sendToTarget();

<span class="hljs-keyword">if</span> (!app.persistent || app.isolated) {
    removeProcessNameLocked(app.processName, app.uid);
    <span class="hljs-keyword">if</span> (mHeavyWeightProcess == app) {
        mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
                mHeavyWeightProcess.userId, <span class="hljs-number">0</span>));
        mHeavyWeightProcess = <span class="hljs-keyword">null</span>;
    }
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!app.removed) {
    <span class="hljs-comment">//對於persistent應用,則需要重啟</span>
    <span class="hljs-keyword">if</span> (mPersistentStartingProcesses.indexOf(app) &lt; <span class="hljs-number">0</span>) {
        mPersistentStartingProcesses.add(app);
        restart = <span class="hljs-keyword">true</span>;
    }
}

<span class="hljs-comment">//mProcessesOnHold:記錄著試圖在系統ready之前就啟動的進程。</span>
<span class="hljs-comment">//在那時並不啟動這些進程,先記錄下來,等系統啟動完成則啟動這些進程。</span>
mProcessesOnHold.remove(app);

<span class="hljs-keyword">if</span> (app == mHomeProcess) {
    mHomeProcess = <span class="hljs-keyword">null</span>;
}
<span class="hljs-keyword">if</span> (app == mPreviousProcess) {
    mPreviousProcess = <span class="hljs-keyword">null</span>;
}

<span class="hljs-keyword">if</span> (restart &amp;&amp; !app.isolated) {
    <span class="hljs-comment">//仍有組件需要運行在該進程中,因此重啟該進程</span>
    <span class="hljs-keyword">if</span> (index &lt; <span class="hljs-number">0</span>) {
        ProcessList.remove(app.pid);
    }
    addProcessNameLocked(app);
    startProcessLocked(app, <span class="hljs-string">"restart"</span>, app.processName);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (app.pid &gt; <span class="hljs-number">0</span> &amp;&amp; app.pid != MY_PID) {
    <span class="hljs-comment">//移除該進程相關信息</span>
    <span class="hljs-keyword">boolean</span> removed;
    <span class="hljs-keyword">synchronized</span> (mPidsSelfLocked) {
        mPidsSelfLocked.remove(app.pid);
        mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
    }
    app.setPid(<span class="hljs-number">0</span>);
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;

}

對於需要重啟進程的情形有:

  • mLaunchingProviders:記錄著存在client端等待的ContentProvider。應用當前正在啟動中,當ContentProvider一旦發佈則將該ContentProvider將從該list去除。當進程包含這樣的ContentProvider,則需要重啟進程。
  • mPersistentStartingProcesses:記錄著試圖在系統ready之前就啟動的進程。在那時並不啟動這些進程,先記錄下來,等系統啟動完成則啟動這些進程。當進程屬於這種類型也需要重啟。

5. 小結

當crash進程執行kill操作後,進程被殺。此時需要掌握binder 死亡通知原理,由於Crash進程中擁有一個Binder服務端ApplicationThread,而應用進程在創建過程調用attachApplicationLocked(),從而attach到system_server進程,在system_server進程內有一個ApplicationThreadProxy,這是相對應的Binder客戶端。當Binder服務端ApplicationThread所在進程(即Crash進程)掛掉後,則Binder客戶端能收到相應的死亡通知,從而進入binderDied流程。更多關於bInder原理,這裡就不細說,博客中有關於binder系列的專題。

binder_died

四、 總結

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

  1. 首先發生crash所在進程,在創建之初便準備好了defaultUncaughtHandler,用來來處理Uncaught Exception,並輸出當前crash基本信息;
  2. 調用當前進程中的AMP.handleApplicationCrash;經過binder ipc機制,傳遞到system_server進程;
  3. 接下來,進入system_server進程,調用binder服務端執行AMS.handleApplicationCrash;
  4. mProcessNames查找到目標進程的ProcessRecord對象;並將進程crash信息輸出到目錄/data/system/dropbox
  5. 執行makeAppCrashingLocked
    • 創建當前用戶下的crash應用的error receiver,並忽略當前應用的廣播;
    • 停止當前進程中所有activity中的WMS的凍結屏幕消息,並執行相關一些屏幕相關操作;
  6. 再執行handleAppCrashLocked方法,
    • 當1分鐘內同一進程連續crash兩次時,且非persistent進程,則直接結束該應用所有activity,並殺死該進程以及同一個進程組下的所有進程。然後再恢復棧頂第一個非finishing狀態的activity;
    • 當1分鐘內同一進程連續crash兩次時,且persistent進程,,則只執行恢復棧頂第一個非finishing狀態的activity;
    • 當1分鐘內同一進程未發生連續crash兩次時,則執行結束棧頂正在運行activity的流程。
  7. 通過mUiHandler發送消息SHOW_ERROR_MSG,彈出crash對話框;
  8. 到此,system_server進程執行完成。回到crash進程開始執行殺掉當前進程的操作;
  9. 當crash進程被殺,通過binder死亡通知,告知system_server進程來執行appDiedLocked();
  10. 最後,執行清理應用相關的activity/service/ContentProvider/receiver組件信息。

這基本就是整個應用Crash後系統的執行過程。 最後,再說說對於同一個app連續crash的情況:

  • 當60s內連續crash兩次的非persistent進程時,被認定為bad進程:那麼如果第3次從後臺啟動該進程(Intent.getFlags來判斷),則會拒絕創建進程;
  • 當crash次數達到兩次的非persistent進程時,則再次殺該進程,即便允許自啟的service也會在被殺後拒絕再次啟動。

书籍推荐