Android8.0系統對異常的處理流程

Android8.0 系統異常處理流程

異常處理流程

Java處理未捕獲異常有個Thread.UncaughtExceptionHandler,在Android系統中當然也是通過實現其來進行未捕獲異常處理。

Android 默認系統異常處理是在啓動SystemServer進程時設置的。

Zygote進程啓動SystemServer時會調用ZygoteInit的forkSystemServer()方法,該方法中又通過handleSystemServerProcess()方法來對SystemServer進程做一些處理,最後會調用到RuntimeInit.commonInit()方法

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

protected static final void commonInit() {
    Thread.setUncaughtExceptionPreHandler(new LoggingHandler());
    // 該出就設置了默認未捕獲異常的處理Handler-KillApplicationHandler
    Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
   ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

KillApplicationHandler代碼如下

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
    public void uncaughtException(Thread t, Throwable e) {
        try {
            ...
            // 1. mApplicationObject標識當前應用
            ActivityManager.getService().handleApplicationCrash(
                    mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
        } ...
        finally {
            // 無論如何都要保證出現crash的進程不存活
            Process.killProcess(Process.myPid());
            System.exit(10);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

註釋1處的ActivityManager.getService()得到的就是ActivityManagerService的服務端代理對象,實現是通過Binder機制。看看AMS在handleApplicationCrash方法中是如何處理的

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public void handleApplicationCrash(IBinder app,
        ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
    ProcessRecord r = findAppProcess(app, "Crash");
    final String processName = app == null ? "system_server"
            : (r == null ? "unknown" : r.processName);
<span class="token function">handleApplicationCrashInner</span><span class="token punctuation">(</span><span class="token string">"crash"</span><span class="token punctuation">,</span> r<span class="token punctuation">,</span> processName<span class="token punctuation">,</span> crashInfo<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
// 1. 將crash信息寫入event log中
EventLog.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);

<span class="token function">addErrorToDropBox</span><span class="token punctuation">(</span>eventType<span class="token punctuation">,</span> r<span class="token punctuation">,</span> processName<span class="token punctuation">,</span> null<span class="token punctuation">,</span> null<span class="token punctuation">,</span> null<span class="token punctuation">,</span> null<span class="token punctuation">,</span> null<span class="token punctuation">,</span> crashInfo<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 2. </span>
mAppErrors<span class="token punctuation">.</span><span class="token function">crashApplication</span><span class="token punctuation">(</span>r<span class="token punctuation">,</span> crashInfo<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

註釋1處將log記錄在event log中。註釋2處調用AppError的crashApplication方法

frameworks/base/services/core/java/com/android/server/am/AppErrors.java

void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
    final int callingPid = Binder.getCallingPid();
    final int callingUid = Binder.getCallingUid();
<span class="token keyword">final</span> <span class="token keyword">long</span> origId <span class="token operator">=</span> Binder<span class="token punctuation">.</span><span class="token function">clearCallingIdentity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
    <span class="token comment">// 調用內部的crashApplicationInner</span>
    <span class="token function">crashApplicationInner</span><span class="token punctuation">(</span>r<span class="token punctuation">,</span> crashInfo<span class="token punctuation">,</span> callingPid<span class="token punctuation">,</span> callingUid<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
    Binder<span class="token punctuation">.</span><span class="token function">restoreCallingIdentity</span><span class="token punctuation">(</span>origId<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

繼續看crashApplicationInner方法

frameworks/base/services/core/java/com/android/server/am/AppErrors.java

void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
        int callingPid, int callingUid) {
    ...
    synchronized (mService) {
        // 1. 處理有IActivityController的情況,如果Controller已經處理錯誤,則不會顯示錯誤框
        if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
                timeMillis, callingPid, callingUid)) {
            return;
        }
        ...
        AppErrorDialog.Data data = new AppErrorDialog.Data();
        data.result = result;
        data.proc = r;
        ...
        // 2. 發送SHOW_ERROR_UI_MSG給AMS的mUiHandler,將彈出一個錯誤對話框,提示用戶某進程crash
        final Message msg = Message.obtain();
        msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
    task = data.task;
    msg.obj = data;
    mService.mUiHandler.sendMessage(msg);
}
// 3. 調用AppErrorResult的get方法,該方法內部調用了wait方法,故爲阻塞狀態,當用戶處理了對話框後會調用AppErrorResult的set方法,該方法內部調用了notifyAll()方法來喚醒線程。
// 注意此處涉及了兩個線程的工作,crashApplicationInner函數工作在Binder調用所在的線程;對話框工作於AMS的Ui線程

int res = result.get();

Intent appErrorIntent = null;
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
// 4. 判斷用戶操作結果,然後根據結果做不同處理
if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
    res = AppErrorDialog.FORCE_QUIT;
}
synchronized (mService) {
    // 不在提示錯誤
    if (res == AppErrorDialog.MUTE) {
        stopReportingCrashesLocked(r);
    }
    // 嘗試重啓進程
    if (res == AppErrorDialog.RESTART) {
        mService.removeProcessLocked(r, false, true, "crash");
        if (task != null) {
            try {
                mService.startActivityFromRecents(task.taskId,
                        ActivityOptions.makeBasic().toBundle());
            } ...
        }
    }
    // 強行結束進程
    if (res == AppErrorDialog.FORCE_QUIT) {
        long orig = Binder.clearCallingIdentity();
        try {
            // Kill it with fire!
            mService.mStackSupervisor.handleAppCrashLocked(r);
            if (!r.persistent) {
                mService.removeProcessLocked(r, false, false, "crash");
                mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
            }
        } finally {
            Binder.restoreCallingIdentity(orig);
        }
    }
    // 停止進程並報告錯誤
    if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
        appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
    }
    ...
}

if (appErrorIntent != null) {
    try {
        // 啓動報告錯誤界面
        mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
    } catch (ActivityNotFoundException e) {
        Slog.w(TAG, "bug report receiver dissappeared", e);
    }
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

註釋1會優先讓crash觀察者進行crash處理,crash觀察者通過AMS的setActivityController()方法進行設置,如果已經處理則不會再彈出錯誤對話框。註釋2會發送SHOW_ERROR_UI_MSG消息給AMS的mUIHandler處理來請求彈出錯誤對話框。註釋3通過調用AppErrorResult中的get()方法來使線程阻塞。需要注意的是此處涉及到兩個線程,crashApplicationInner工作在Binder調用所在的線程,對話框顯示則處於AMS的UI線程。具體AppErrorResult的工作後面會說到。待用戶操作對話框後或者超時時間到時get()方法就會被喚醒,並且返回處理結果。註釋4則根據用戶操作結果進行不同的處理,例如強制停止進程,重啓進程等。

這裏看下注釋2處是如何顯示錯誤對話框的,AMS的UiHandler接收到了消息就會進行顯示操作

crash對話框的顯示和用戶行爲

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

final class UiHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        // 顯示錯誤對話框
        case SHOW_ERROR_UI_MSG: {
            mAppErrors.handleShowAppErrorUi(msg);
            ensureBootCompleted();
        } break;
        // 顯示ANR對話框
        case SHOW_NOT_RESPONDING_UI_MSG: {
            mAppErrors.handleShowAnrUi(msg);
            ensureBootCompleted();
        } break;
        ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可以看到UiHandler對錯誤和ANR對話框顯示的處理,這裏看錯誤對話框的顯示,其還是通過AppErrors類進行處理。

frameworks/base/services/core/java/com/android/server/am/AppErrors.java

void handleShowAppErrorUi(Message msg) {
    ...
    synchronized (mService) {
        ProcessRecord proc = data.proc;
        AppErrorResult res = data.result;
        // 1. crash 對話框已顯示,故無需再顯示
        if (proc != null && proc.crashDialog != null) {
            if (res != null) {
                res.set(AppErrorDialog.ALREADY_SHOWING);
            }
            return;
        }
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token keyword">final</span> <span class="token keyword">boolean</span> crashSilenced <span class="token operator">=</span> mAppsNotReportingCrashes <span class="token operator">!=</span> null <span class="token operator">&amp;&amp;</span>
            mAppsNotReportingCrashes<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>proc<span class="token punctuation">.</span>info<span class="token punctuation">.</span>packageName<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>mService<span class="token punctuation">.</span><span class="token function">canShowErrorDialogs</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">||</span> showBackground<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>crashSilenced<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 2. 創建crash對話框</span>
        proc<span class="token punctuation">.</span>crashDialog <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AppErrorDialog</span><span class="token punctuation">(</span>mContext<span class="token punctuation">,</span> mService<span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token comment">// 3. 如果AMS禁止顯示錯誤對話框,或者當前設備處於睡眠模式則不會讓顯示對話框</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>res <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            res<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>AppErrorDialog<span class="token punctuation">.</span>CANT_SHOW<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 4. 調用Dialog show方法顯示crash對話框</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>proc<span class="token punctuation">.</span>crashDialog <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    data<span class="token punctuation">.</span>proc<span class="token punctuation">.</span>crashDialog<span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

註釋1先對crash進程是否已經顯示對話框做了判斷,如果已經顯示則無需顯示。註釋2處,手機沒有息屏,AMS也允許顯示crash對話框,則創建對話框,否則走註釋3處,直接說明不顯示。如果走到註釋4則需要顯示crash對話框,故直接調用Dialog的show()方法。這裏對註釋1和註釋3處的res.set()方法做以解釋,這res就是AppErrorResult,也就是在crashApplicationInner方法中創建的,該方法在請求AMS顯示對話框時調用了result.get()使其阻塞,調用set方法後則會喚醒Binder調用線程,接着走下面代碼,進而對結果進行判斷。

看下AppErrorResult get()和set()的實現

frameworks/base/services/core/java/com/android/server/am/AppErrorResult.java

final class AppErrorResult {
    public void set(int res) {
        synchronized (this) {
            mHasResult = true;
            // 1. set方法設置mResult的值
            mResult = res;
            // 2.  調用notifyAll喚醒持有當前對象鎖且處於阻塞狀態的所有線程
            notifyAll();
        }
    }
<span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>mHasResult<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">try</span> <span class="token punctuation">{</span>
                <span class="token comment">//3. 實質通過wait()使當前線程阻塞</span>
                <span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 4. 返回mResult</span>
    <span class="token keyword">return</span> mResult<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">boolean</span> mHasResult <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> mResult<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

通過get()方法線程阻塞,通過set方法更新mResult的值並喚醒處於等待隊列的線程,此時接着get()方法wait後面的代碼執行,將set()方法中更新的mResult值作爲返回值。

當錯誤對話框彈出後,用戶操作或者超時時間到時又是怎樣的?我們一起看下AppErrorDialog

frameworks/base/services/core/java/com/android/server/am/AppErrorDialog.java

@Override
public void onClick(View v) {
    // 1. 判斷點擊控件,來決定操作
    switch (v.getId()) {
        // 請求重啓進程
        case com.android.internal.R.id.aerr_restart:
            mHandler.obtainMessage(RESTART).sendToTarget();
            break;
        // 請求反饋報錯問題
        case com.android.internal.R.id.aerr_report:
            mHandler.obtainMessage(FORCE_QUIT_AND_REPORT).sendToTarget();
            break;
        // 請求關閉crash Dialog並殺死進程
        case com.android.internal.R.id.aerr_close:
            mHandler.obtainMessage(FORCE_QUIT).sendToTarget();
            break;
        // 請求不再提示對話框
        case com.android.internal.R.id.aerr_mute:
            mHandler.obtainMessage(MUTE).sendToTarget();
            break;
        default:
            break;
    }
}

// 2. 受到請求信息後調用setResult()方法並關閉對話框
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
setResult(msg.what);
dismiss();
}
};
private void setResult(int result) {
synchronized (mService) {
if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
mProc.crashDialog = null;
}
}
// 3. 調用AppErrorResult的set方法使阻塞線程運行,並將用戶點擊結果告知
mResult.set(result);

mHandler<span class="token punctuation">.</span><span class="token function">removeMessages</span><span class="token punctuation">(</span>TIMEOUT<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

註釋的步驟寫的已經很清楚了,最終通過mResult.set()方法喚線程,是線程代碼接着執行

frameworks/base/services/core/java/com/android/server/am/AppErrors.java

void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
        int callingPid, int callingUid) {
    ...
    // 3. 阻塞線程直至超時或者用戶操作對話框
    int res = result.get();
    // 4. 判斷用戶操作結果,然後根據結果做不同處理
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

後續清理工作

根據前面的流程,我們知道當進程crash後,最終將被kill掉,此時AMS還需要完成後續的清理工作。

我們先來回憶一下進程啓動後,註冊到AMS的部分流程

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

// 進程啓動後,對應的ActivityThread會attach到AMS上
private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
    ...
    final String processName = app.processName;
    try {
        // 1.  創建“訃告”接收者
        AppDeathRecipient adr = new AppDeathRecipient(
                app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
        app.deathRecipient = adr;
    } 
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

當進程註冊到AMS時,AMS註冊了一個“訃告”接收者註冊到進程中。
因此,當crash進程被kill後,AppDeathRecipient中的binderDied方法將被回調。看源碼知道bindDied()方法中又會調用到appDiedLocked()方法

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
        boolean fromBinderDied) {
    ...
    // 1. 該進程沒有殺死,則殺死進程
    if (!app.killed) {
        if (!fromBinderDied) {
            killProcessQuiet(pid);
        }
        killProcessGroup(app.uid, pid);
        app.killed = true;
    }
<span class="token keyword">if</span> <span class="token punctuation">(</span>app<span class="token punctuation">.</span>pid <span class="token operator">==</span> pid <span class="token operator">&amp;&amp;</span> app<span class="token punctuation">.</span>thread <span class="token operator">!=</span> null <span class="token operator">&amp;&amp;</span>
        app<span class="token punctuation">.</span>thread<span class="token punctuation">.</span><span class="token function">asBinder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> thread<span class="token punctuation">.</span><span class="token function">asBinder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token comment">// 2. </span>
    <span class="token function">handleAppDiedLocked</span><span class="token punctuation">(</span>app<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

註釋1會將進程殺死,註釋2處爲app死亡的關鍵處理

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

private final void handleAppDiedLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart) {
    int pid = app.pid;
    // 1. 進行進程中service、ContentProvider、BroadcastReceiver等的收尾工作
    boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
            false /*replacingPid*/);
    if (!kept && !restarting) {
        removeLruProcessLocked(app);
        if (pid > 0) {
            ProcessList.remove(pid);
        }
    }
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token comment">// 2. 判斷是否還存在可見的Activity</span>
<span class="token keyword">boolean</span> hasVisibleActivities <span class="token operator">=</span> mStackSupervisor<span class="token punctuation">.</span><span class="token function">handleAppDiedLocked</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 清除activity列表</span>
app<span class="token punctuation">.</span>activities<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>restarting <span class="token operator">&amp;&amp;</span> hasVisibleActivities
            <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>mStackSupervisor<span class="token punctuation">.</span><span class="token function">resumeFocusedStackTopActivityLocked</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 3. 若當前crash進程中存在可視Activity,那麼AMS還是會確保所有可見Activity正常運行,故會重啓該進程</span>
        mStackSupervisor<span class="token punctuation">.</span><span class="token function">ensureActivitiesVisibleLocked</span><span class="token punctuation">(</span>null<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">!</span>PRESERVE_WINDOWS<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
    mWindowManager<span class="token punctuation">.</span><span class="token function">continueSurfaceLayout</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

註釋1比較重要的是對於crash進程中的Bounded Service而言,會清理掉service與客戶端之間的聯繫,此外若service的客戶端重要性過低,還會被直接kill掉。註釋2處判斷是否應用還存在可見的Activity,註釋3處對於可見的Activity系統要保證其正常運行,還會重新啓動進程。

總結

app停止原來如此啊,當然app停止不可完全避免,但是一旦出現實在太難看了,而且沒法收集到log,下篇就看看作爲開發者自己如何處理這種未捕獲異常。

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