Android后臺(tái)殺死系列之二:ActivityManagerService與App現(xiàn)場恢復(fù)機(jī)制

AMS與后臺(tái)殺死

本篇是Android后臺(tái)殺死系列的第二篇梳毙,主要講解ActivityMangerService是如何恢復(fù)被后臺(tái)殺死的進(jìn)程的(基于4.3 )梧躺,在開篇 FragmentActivity及PhoneWindow后臺(tái)殺死處理機(jī)制 中,簡述了后臺(tái)殺死所引起的一些常見問題砸狞,還有Android系統(tǒng)控件對后臺(tái)殺死所做的一些兼容敛助,以及onSaveInstance跟onRestoreInstance的作用于執(zhí)行時(shí)機(jī)叨粘,最后說了如何應(yīng)對后臺(tái)殺死秒梅,但是對于被后臺(tái)殺死的進(jìn)程如何恢復(fù)的并沒有講解旗芬,本篇不涉及后臺(tái)殺死,比如LowmemoryKiller機(jī)制捆蜀,只講述被殺死的進(jìn)程如何恢復(fù)的唤冈。假設(shè)厦幅,一個(gè)應(yīng)用被后臺(tái)殺死,再次從最近的任務(wù)列表喚起App時(shí)候,系統(tǒng)是如何處理的呢羡滑?有這么幾個(gè)問題可能需要解決:

  • Android框架層(AMS)如何知道App被殺死了
  • App被殺前的場景是如何保存的
  • 系統(tǒng)(AMS)如何恢復(fù)被殺的App
  • 被后臺(tái)殺死的App的啟動(dòng)流程跟普通的啟動(dòng)有什么區(qū)別
  • Activity的恢復(fù)順序?yàn)槭裁词堑剐蚧謴?fù)

系統(tǒng)(AMS)如何知道App被殺死了

首先來看第一個(gè)問題,系統(tǒng)如何知道Application被殺死了呜投,Android使用了Linux的oomKiller機(jī)制锦茁,只是簡單的做了個(gè)變種,采用分等級的LowmemoryKiller洞辣,但這個(gè)其實(shí)是內(nèi)核層面的咐刨,LowmemoryKiller殺死進(jìn)程后,不會(huì)像用戶空間發(fā)送通知扬霜,也就是說框架層的ActivityMangerService無法知道App是否被殺死定鸟,但是,只有知道App或者Activity是否被殺死著瓶,AMS(ActivityMangerService)才能正確的走喚起流程联予,那么AMS究竟是在什么時(shí)候知道App或者Activity被后臺(tái)殺死了呢?我們先看一下從最近的任務(wù)列表進(jìn)行喚起的時(shí)候材原,究竟發(fā)生了什么沸久。

從最近的任務(wù)列表或者Icon再次喚起App的流程

在系統(tǒng)源碼systemUi的包里,有個(gè)RecentActivity余蟹,這個(gè)其實(shí)就是最近的任務(wù)列表的入口卷胯,而其呈現(xiàn)界面是通過RecentsPanelView來展現(xiàn)的,點(diǎn)擊最近的App其執(zhí)行代碼如下:

public void handleOnClick(View view) {
    ViewHolder holder = (ViewHolder)view.getTag();
    TaskDescription ad = holder.taskDescription;
    final Context context = view.getContext();
    final ActivityManager am = (ActivityManager)
            context.getSystemService(Context.ACTIVITY_SERVICE);
    Bitmap bm = holder.thumbnailViewImageBitmap;
    ...
    // 關(guān)鍵點(diǎn) 1  如果TaskDescription沒有被主動(dòng)關(guān)閉威酒,正常關(guān)閉窑睁,ad.taskId就是>=0
    if (ad.taskId >= 0) {
        // This is an active task; it should just go to the foreground.
        am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME,
                opts);
    } else {
        Intent intent = ad.intent;
        intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
                | Intent.FLAG_ACTIVITY_TASK_ON_HOME
                | Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            context.startActivityAsUser(intent, opts,
                    new UserHandle(UserHandle.USER_CURRENT));
        }...
}

在上面的代碼里面葵孤,有個(gè)判斷ad.taskId >= 0,如果滿足這個(gè)條件裳朋,就通過moveTaskToFront喚起APP吓著,那么ad.taskId是如何獲取的?recent包里面有各類RecentTasksLoader绑莺,這個(gè)類就是用來加載最近任務(wù)列表的一個(gè)Loader,看一下它的源碼诫肠,主要看一下加載:

 @Override
        protected Void doInBackground(Void... params) {
            // We load in two stages: first, we update progress with just the first screenful
            // of items. Then, we update with the rest of the items
            final int origPri = Process.getThreadPriority(Process.myTid());
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            final PackageManager pm = mContext.getPackageManager();
            final ActivityManager am = (ActivityManager)
            mContext.getSystemService(Context.ACTIVITY_SERVICE);

            final List<ActivityManager.RecentTaskInfo> recentTasks =
                    am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
             
            ....
                TaskDescription item = createTaskDescription(recentInfo.id,
                        recentInfo.persistentId, recentInfo.baseIntent,
                        recentInfo.origActivity, recentInfo.description);
            ....
            } 

可以看到司澎,其實(shí)就是通過ActivityManger的getRecentTasks向AMS請求最近的任務(wù)信息,然后通過createTaskDescription創(chuàng)建TaskDescription栋豫,這里傳遞的recentInfo.id其實(shí)就是TaskDescription的taskId挤安,來看一下它的意義:

public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
        int flags, int userId) {
        ...           
        IPackageManager pm = AppGlobals.getPackageManager();

        final int N = mRecentTasks.size();
        ...
        for (int i=0; i<N && maxNum > 0; i++) {
            TaskRecord tr = mRecentTasks.get(i);
            if (i == 0
                    || ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
                    || (tr.intent == null)
                    || ((tr.intent.getFlags()
                            &Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0)) {
                ActivityManager.RecentTaskInfo rti
                        = new ActivityManager.RecentTaskInfo();
                rti.id = tr.numActivities > 0 ? tr.taskId : -1;
                rti.persistentId = tr.taskId;
                rti.baseIntent = new Intent(
                        tr.intent != null ? tr.intent : tr.affinityIntent);
                if (!detailed) {
                    rti.baseIntent.replaceExtras((Bundle)null);
                }

可以看出RecentTaskInfo的id是由TaskRecord決定的,如果TaskRecord中numActivities > 0就去TaskRecord的Id丧鸯,否則就取-1蛤铜,這里的numActivities其實(shí)就是TaskRecode中記錄的ActivityRecord的數(shù)目,更具體的細(xì)節(jié)可以自行查看ActivityManagerService及ActivityStack丛肢,那么這里就容易解釋了围肥,只要是存活的APP、或者被LowmemoryKiller殺死的APP蜂怎,其AMS的ActivityRecord是完整保存的穆刻,這就是恢復(fù)的依據(jù)。RecentActivity獲取的數(shù)據(jù)其實(shí)就是AMS中的翻版杠步,RecentActivity并不知道將要喚起的APP是否是存活的氢伟,只要TaskRecord告訴RecentActivity是存貨的,那么久直接走喚起流程篮愉,也就是通過ActivityManager的moveTaskToFront喚起App腐芍,至于后續(xù)的工作,就完全交給AMS來處理∈怎铮現(xiàn)看一下到這里的流程圖:

從最近的任務(wù)列表喚起App的流程

整個(gè)APP被后臺(tái)殺死的情況下AMS是如何恢復(fù)現(xiàn)場的

AMS與客戶端的通信是通過Binder來進(jìn)行的猪勇,并且通信是”全雙工“的,且互為客戶端跟服務(wù)器颠蕴,也就說AMS向客戶端發(fā)命令的時(shí)候,AMS是客戶端椅您,反之亦然掀泳。注意 Binder有個(gè)訃告的功能的:如果基于Binder通信的服務(wù)端(S)如果掛掉了员舵,客戶端(C)能夠收到Binder驅(qū)動(dòng)發(fā)送的一份訃告,告知客戶端Binder服務(wù)掛了韭邓,可以把Binder驅(qū)動(dòng)看作是第三方不死郵政機(jī)構(gòu)女淑,專門向客戶端發(fā)偶像死亡通知鸭你。對于APP被異常殺死的情況下,這份訃告是發(fā)送給AMS的瓣窄,AMS在收到通知后俺夕,就會(huì)針對APP被異常殺死的情況作出整理,這里牽扯到Binder驅(qū)動(dòng)的代碼有興趣可以自己翻一下映九。之類直接沖訃告接受后端處理邏輯來分析,在AMS源碼中件甥,入口其實(shí)就是appDiedLocked.

final void appDiedLocked(ProcessRecord app, int pid,
            IApplicationThread thread) {
              ...
            if (app.pid == pid && app.thread != null &&
                app.thread.asBinder() == thread.asBinder()) {
            boolean doLowMem = app.instrumentationClass == null;
            關(guān)鍵點(diǎn)1 
            handleAppDiedLocked(app, false, true);
             // 如果是被后臺(tái)殺了,怎么處理
             關(guān)鍵點(diǎn)2 
            if (doLowMem) {
                boolean haveBg = false;
                for (int i=mLruProcesses.size()-1; i>=0; i--) {
                    ProcessRecord rec = mLruProcesses.get(i);
                    if (rec.thread != null && rec.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
                        haveBg = true;
                        break;
                    }
                }
                if (!haveBg) {
                <!--如果被LowmemoryKiller殺了譬正,就說明內(nèi)存緊張曾我,這個(gè)時(shí)候就會(huì)通知其他后臺(tái)APP,小心了虐秦,趕緊釋放資源-->
                    EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
                    long now = SystemClock.uptimeMillis();
                    for (int i=mLruProcesses.size()-1; i>=0; i--) {
                        ProcessRecord rec = mLruProcesses.get(i);
                        if (rec != app && rec.thread != null &&
                                (rec.lastLowMemory+GC_MIN_INTERVAL) <= now) {
                            if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
                                rec.lastRequestedGc = 0;
                            } else {
                                rec.lastRequestedGc = rec.lastLowMemory;
                            }
                            rec.reportLowMemory = true;
                            rec.lastLowMemory = now;
                            mProcessesToGc.remove(rec);
                            addProcessToGcListLocked(rec);
                        }
                    }
                    mHandler.sendEmptyMessage(REPORT_MEM_USAGE);
                    <!--縮減資源-->
                    scheduleAppGcsLocked();
                }
            }
        }
        ...
    }

先看關(guān)鍵點(diǎn)1:在進(jìn)程被殺死后蜈彼,AMS端要選擇性清理進(jìn)程相關(guān)信息幸逆,清理后还绘,再根據(jù)是不是內(nèi)存低引起的后臺(tái)殺死,決定是不是需要清理其他后臺(tái)進(jìn)程昔案。接著看handleAppDiedLocked如何清理的踏揣,這里有重建時(shí)的依據(jù):ActivityRecord不清理,但是為它設(shè)置個(gè)APP未綁定的標(biāo)識(shí)

private final void handleAppDiedLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart) {
    
    關(guān)鍵點(diǎn)1
    cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
    ...
    關(guān)鍵點(diǎn)2
     // Remove this application's activities from active lists.
    boolean hasVisibleActivities = mMainStack.removeHistoryRecordsForAppLocked(app);

    app.activities.clear();
    ...
     關(guān)鍵點(diǎn)3
    if (!restarting) {
        if (!mMainStack.resumeTopActivityLocked(null)) {
            // If there was nothing to resume, and we are not already
            // restarting this process, but there is a visible activity that
            // is hosted by the process...  then make sure all visible
            // activities are running, taking care of restarting this
            // process.
            if (hasVisibleActivities) {
                mMainStack.ensureActivitiesVisibleLocked(null, 0);
            }
        }
    }
}

看關(guān)鍵點(diǎn)1括享,cleanUpApplicationRecordLocked,主要負(fù)責(zé)清理一些Providers娇斩,receivers犬第,service之類的信息歉嗓,并且在清理過程中根據(jù)配置的一些信息決定是否需要重建進(jìn)程并啟動(dòng)哮幢,關(guān)鍵點(diǎn)2 就是關(guān)系到喚起流程的判斷橙垢,關(guān)鍵點(diǎn)3柜某,主要是被殺的進(jìn)程是否是當(dāng)前前臺(tái)進(jìn)程,如果是惭等,需要重建,并立即顯示:先簡單看cleanUpApplicationRecordLocked的清理流程

 private final void cleanUpApplicationRecordLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart, int index) {
        
   <!--清理service-->
    mServices.killServicesLocked(app, allowRestart);
    ...
    boolean restart = false;
   <!--清理Providers.-->
    if (!app.pubProviders.isEmpty()) {
        Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
        while (it.hasNext()) {
            ContentProviderRecord cpr = it.next();
            寡具。。厦坛。
        app.pubProviders.clear();
    } ...
     <!--清理receivers.-->
     // Unregister any receivers.
    if (app.receivers.size() > 0) {
        Iterator<ReceiverList> it = app.receivers.iterator();
        while (it.hasNext()) {
            removeReceiverLocked(it.next());
        }
        app.receivers.clear();
    }
    ... 關(guān)鍵點(diǎn)1杜秸,進(jìn)程是夠需要重啟撬碟,
    if (restart && !app.isolated) {
        // We have components that still need to be running in the
        // process, so re-launch it.
        mProcessNames.put(app.processName, app.uid, app);
        startProcessLocked(app, "restart", app.processName);
    } 
     ...
}

從關(guān)鍵點(diǎn)1就能知道,這里是隱藏了進(jìn)程是否需要重啟的邏輯其障,比如一個(gè)Service設(shè)置了START_STICKY励翼,被殺后扶认,就需要重新喚起辐宾,這里也是流氓軟件肆虐的原因叠纹。再接著看mMainStack.removeHistoryRecordsForAppLocked(app),對于直觀理解APP重建
持偏,這句代碼處于核心的地位鸿秆,

boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
    ...
    while (i > 0) {
        i--;
        ActivityRecord r = (ActivityRecord)mHistory.get(i);
        if (r.app == app) {
            boolean remove;
            <!--關(guān)鍵點(diǎn)1-->
            if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
                remove = true;
            } else if (r.launchCount > 2 &&
                remove = true;
            } else {
             //一般來講卿叽,走false
                remove = false;
            }
            <!--關(guān)鍵點(diǎn)2-->
            if (remove) {
                ...
                removeActivityFromHistoryLocked(r);

            } else {
                ...
                r.app = null;
                ...
    }

    return hasVisibleActivities;
}

在Activity跳轉(zhuǎn)的時(shí)候,準(zhǔn)確的說催烘,在stopActivity之前伊群,會(huì)保存Activity的現(xiàn)場奔则,這樣在AMS端r.haveState==true易茬,也就是說范抓,其ActivityRecord不會(huì)被從ActivityStack中移除匕垫,同時(shí)ActivityRecord的app字段被置空象泵,這里在恢復(fù)的時(shí)候,是決定resume還是重建的關(guān)鍵朗涩。接著往下看moveTaskToFrontLocked兄一,這個(gè)函數(shù)在ActivityStack中瘾腰,主要管理ActivityRecord棧的,所有start的Activity都在ActivityStack中保留一個(gè)ActivityRecord硝全,這個(gè)也是AMS管理Activiyt的一個(gè)依據(jù),最終moveTaskToFrontLocked會(huì)調(diào)用resumeTopActivityLocked來喚起Activity召廷,AMS獲取即將resume的Activity信息的方式主要是通過ActivityRecord先紫,它并不知道Activity本身是否存活遮精,獲取之后准脂,AMS在喚醒Activity的環(huán)節(jié)才知道App或者Activity被殺死狸膏,具體看一下resumeTopActivityLocked源碼:

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
          ...
         關(guān)鍵點(diǎn)1 
        if (next.app != null && next.app.thread != null) { 
        ...
        
        } else {
            // Whoops, need to restart this activity!
          ...
            startSpecificActivityLocked(next, true, true);
        }

        return true;
    }

看關(guān)鍵點(diǎn)1的判斷條件,由于已經(jīng)將ActivityRecord的app字段置空院塞,AMS就知道了這個(gè)APP或者Activity被異常殺死過拦止,因此汹族,就會(huì)走startSpecificActivityLocked進(jìn)行重建顶瞒。 其實(shí)仔細(xì)想想很簡單,對于非主動(dòng)調(diào)用finish的坑资,AMS不會(huì)清理掉ActivitRecord袱贮,在喚起APP的時(shí)候,如果AMS檢測到APP還存活荒勇,就走scheduleResumeActivity進(jìn)行喚起上一個(gè)Activity常柄,但是如果APP或者Activity被異常殺死過卷玉,那么AMS就通過startSpecificActivityLocked再次將APP重建,并且將最后的Activity重建寝并。

APP存活衬潦,但是Activity被后臺(tái)殺死的情況下AMS是如何恢復(fù)現(xiàn)場的

還有一種可能,APP沒有被kill漂羊,但是Activity被Kill掉了走越,這個(gè)時(shí)候會(huì)怎么樣旨指?首先,Activity的管理是一定通過AMS的,Activity的kill一定是是AMS操刀的瞬项,是有記錄的囱淋,嚴(yán)格來說餐塘,這種情況并不屬于后臺(tái)殺死妥衣,因?yàn)檫@屬于AMS正常的管理,在可控范圍,比如打開了開發(fā)者模式中的“不保留活動(dòng)”,這個(gè)時(shí)候税手,雖然會(huì)殺死Activity蜂筹,但是仍然保留了ActivitRecord,所以再喚醒芦倒,或者回退的的時(shí)候仍然有跡可循,看一下ActivityStack的Destroy回調(diào)代碼艺挪,

  final boolean destroyActivityLocked(ActivityRecord r,
            boolean removeFromApp, boolean oomAdj, String reason) {
        ...
        if (hadApp) {
          ...
           boolean skipDestroy = false;
            try {
             關(guān)鍵代碼 1
                r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
                        r.configChangeFlags);
            ...
            if (r.finishing && !skipDestroy) {
                if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYING: " + r
                        + " (destroy requested)");
                r.state = ActivityState.DESTROYING;
                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
                msg.obj = r;
                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
            } else {
          關(guān)鍵代碼 2
                r.state = ActivityState.DESTROYED;
                if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity " + r);
                r.app = null;
            }
        } 
        return removedFromHistory;
    }  

這里有兩個(gè)關(guān)鍵啊你單器钟,1是告訴客戶端的AcvitityThread清除Activity狞谱,2是標(biāo)記如果AMS自己非正常關(guān)閉的Activity掰读,就將ActivityRecord的state設(shè)置為ActivityState.DESTROYED拢肆,并且清空它的ProcessRecord引用:r.app = null。這里是喚醒時(shí)候的一個(gè)重要標(biāo)志,通過這里AMS就能知道Activity被自己異常關(guān)閉了,設(shè)置ActivityState.DESTROYED是為了讓避免后面的清空邏輯僚祷。

final void activityDestroyed(IBinder token) {
    synchronized (mService) {
        final long origId = Binder.clearCallingIdentity();
        try {
            ActivityRecord r = ActivityRecord.forToken(token);
            if (r != null) {
                mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
            }
           int index = indexOfActivityLocked(r);
            if (index >= 0) {
            1  <!--這里會(huì)是否從history列表移除ActivityRecord-->
                if (r.state == ActivityState.DESTROYING) {
                    cleanUpActivityLocked(r, true, false);
                    removeActivityFromHistoryLocked(r);
                }
            }
            resumeTopActivityLocked(null);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
}

看代碼關(guān)鍵點(diǎn)1蜕琴,只有r.state == ActivityState.DESTROYING的時(shí)候雏搂,才會(huì)移除ActivityRecord诲祸,但是對于不非正常finish的Activity午阵,其狀態(tài)是不會(huì)被設(shè)置成ActivityState.DESTROYING于个,是直接跳過了ActivityState.DESTROYING或链,被設(shè)置成了ActivityState.DESTROYED粒没,所以不會(huì)removeActivityFromHistoryLocked,也就是保留了ActivityRecord現(xiàn)場,好像也是依靠異常來區(qū)分是否是正常的結(jié)束掉Activity。這種情況下是如何啟動(dòng)Activity的呢跋核? 通過上面兩點(diǎn)分析智什,就知道了兩個(gè)關(guān)鍵點(diǎn)

  1. ActivityRecord沒有動(dòng)HistoryRecord列表中移除
  2. ActivityRecord 的ProcessRecord字段被置空看蚜,r.app = null

這樣就保證了在resumeTopActivityLocked的時(shí)候,走startSpecificActivityLocked分支

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
          ...
            
        if (next.app != null && next.app.thread != null) { 
        ...
        
        } else {
            // Whoops, need to restart this activity!
          ...
            startSpecificActivityLocked(next, true, true);
        }

        return true;
    }

到這里,AMS就知道了這個(gè)APP或者Activity是不是被異常殺死過,從而炭臭,決定是走resume流程還是restore流程吸申。

App被殺前的場景是如何保存的: 新Activity啟動(dòng)跟舊Activity的保存

App現(xiàn)場的保存流程相對是比較簡單的齐帚,入口基本就是startActivity的時(shí)候蚓哩,只要是界面的跳轉(zhuǎn)基本都牽扯到Activity的切換跟當(dāng)前Activity場景的保存:先畫個(gè)簡單的圖形抓韩,開偏里面講FragmentActivity的時(shí)候易遣,簡單說了一些onSaveInstance的執(zhí)行時(shí)機(jī)豆茫,這里詳細(xì)看一下AMS是如何管理這些跳轉(zhuǎn)以及場景保存的倦挂,模擬場景:Activity A 啟動(dòng)Activity B的時(shí)候,這個(gè)時(shí)候A不可見,可能會(huì)被銷毀形耗,需要保存A的現(xiàn)場,這個(gè)流程是什么樣的:簡述如下

  • ActivityA startActivity ActivityB
  • ActivityA pause
  • ActivityB create
  • ActivityB start
  • ActivityB resume
  • ActivityA onSaveInstance
  • ActivityA stop

流程大概是如下樣子:

新Activity加載以及前Activity保存流程

現(xiàn)在我們通過源碼一步一步跟一下童社,看看AMS在新Activity啟動(dòng)跟舊Activity的保存的時(shí)候肥矢,到底做了什么:跳過簡單的startActivity,直接去AMS中去看

ActivityManagerService

  public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo,
        String resultWho, int requestCode, int startFlags,
        String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
    enforceNotIsolatedCaller("startActivity");
     ...
    return mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
            resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
            null, null, options, userId);
}

ActivityStack

final int startActivityMayWait(IApplicationThread caller, int callingUid,
                  
        int res = startActivityLocked(caller, intent, resolvedType,
                aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
                callingPackage, startFlags, options, componentSpecified, null);
        
     。。甘改。
} 

這里通過startActivityMayWait啟動(dòng)新的APP旅东,或者新Activity,這里只看簡單的十艾,至于從桌面啟動(dòng)App的流程抵代,可以去參考更詳細(xì)的文章,比如老羅的startActivity流程忘嫉,大概就是新建ActivityRecord荤牍,ProcessRecord之類,并加入AMS中相應(yīng)的堆棧等庆冕,resumeTopActivityLocked是界面切換的統(tǒng)一入口康吵,第一次進(jìn)來的時(shí)候,由于ActivityA還在沒有pause访递,因此需要先暫停ActivityA晦嵌,這些完成后,

ActivityStack

  final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {       
     ...
     <!--必須將當(dāng)前Resume的Activity設(shè)置為pause 然后stop才能繼續(xù)-->
   // We need to start pausing the current activity so the top one
    // can be resumed...
    if (mResumedActivity != null) {            
        if (next.app != null && next.app.thread != null) {
            
            mService.updateLruProcessLocked(next.app, false);
        }
        startPausingLocked(userLeaving, false);
        return true;
        }
        ....?
}

其實(shí)這里就是暫停ActivityA拷姿,AMS通過Binder告訴ActivityThread需要暫停的ActivityA惭载,ActivityThread完成后再通過Binder通知AMS,AMS會(huì)開始resume ActivityB响巢,

private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {

    if (prev.app != null && prev.app.thread != null) {
       ...
        try {
            prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                    userLeaving, prev.configChangeFlags);

ActivityThread

   private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges) {
        ActivityClientRecord r = mActivities.get(token);
        if (r != null) {
            ...
            performPauseActivity(token, finished, r.isPreHoneycomb());
            ...
            // Tell the activity manager we have paused.
            try {
                ActivityManagerNative.getDefault().activityPaused(token);
            } catch (RemoteException ex) {
            }
        }
    }

AMS收到ActivityA發(fā)送過來的pause消息之后描滔,就會(huì)喚起ActivityB,入口還是resumeTopActivityLocked踪古,喚醒B含长,之后還會(huì)A給進(jìn)一步stop掉,這個(gè)時(shí)候就牽扯到現(xiàn)場的保存伏穆,

ActivityStack

 private final void completePauseLocked() {
   
    if (!mService.isSleeping()) {
        resumeTopActivityLocked(prev);
    } else {
    
   ...
}   

ActivityB如何啟動(dòng)的茎芋,本文不關(guān)心,只看ActivityA如何保存現(xiàn)場的蜈出,ActivityB起來后田弥,會(huì)通過ActivityStack的stopActivityLocked去stop ActivityA,

private final void stopActivityLocked(ActivityRecord r) {
       ...
        if (mMainStack) {
             
            r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
        ...
       }    

回看APP端铡原,看一下ActivityThread中的調(diào)用:首先通過callActivityOnSaveInstanceState偷厦,將現(xiàn)場保存到Bundle中去,

    private void performStopActivityInner(ActivityClientRecord r,
        StopInfo info, boolean keepShown, boolean saveState) {
       ...
        // Next have the activity save its current state and managed dialogs...
        if (!r.activity.mFinished && saveState) {
            if (r.state == null) {
                state = new Bundle();
                state.setAllowFds(false);
                mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
                r.state = state;
         燕刻。只泼。。
         }

之后卵洗,通過ActivityManagerNative.getDefault().activityStopped请唱,通知AMS Stop動(dòng)作完成弥咪,在通知的時(shí)候,還會(huì)將保存的現(xiàn)場數(shù)據(jù)帶過去十绑。

private static class StopInfo implements Runnable {
    ActivityClientRecord activity;
    Bundle state;
    Bitmap thumbnail;
    CharSequence description;

    @Override public void run() {
        // Tell activity manager we have been stopped.
        try {

            ActivityManagerNative.getDefault().activityStopped(
                activity.token, state, thumbnail, description);
        } catch (RemoteException ex) {
        }
    }
}

通過上面流程聚至,AMS不僅啟動(dòng)了新的Activity,同時(shí)也將上一個(gè)Activity的現(xiàn)場進(jìn)行了保存本橙,及時(shí)由于種種原因上一個(gè)Actiivity被殺死扳躬,在回退,或者重新喚醒的過程中AMS也能知道如何喚起Activiyt甚亭,并恢復(fù)贷币。

現(xiàn)在解決兩個(gè)問題,1亏狰、如何保存現(xiàn)場役纹,2、AMS怎么判斷知道APP或者Activity是否被異常殺死暇唾,那么就剩下最后一個(gè)問題了促脉,AMS如何恢復(fù)被異常殺死的APP或者Activity呢。

整個(gè)Application被后臺(tái)殺死情況下的恢復(fù)邏輯

其實(shí)在講解AMS怎么判斷知道APP或者Activity是否被異常殺死的時(shí)候信不,就已經(jīng)涉及了恢復(fù)的邏輯嘲叔,也知道了一旦AMS知道了APP被后臺(tái)殺死了亡呵,那就不是正常的resuem流程了抽活,而是要重新laucher,先來看一下整個(gè)APP被干掉的會(huì)怎么處理锰什,看resumeTopActivityLocked部分,從上面的分析已知下硕,這種場景下,會(huì)因?yàn)锽inder通信拋異常走異常分支汁胆,如下:

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
  ....
  if (next.app != null && next.app.thread != null) {
            if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
            ...            
            try {
             ...
            } catch (Exception e) {
                // Whoops, need to restart this activity!
                這里是知道整個(gè)app被殺死的
                Slog.i(TAG, "Restarting because process died: " + next);
                next.state = lastState;
                mResumedActivity = lastResumedActivity;
                Slog.i(TAG, "Restarting because process died: " + next);
              
                startSpecificActivityLocked(next, true, false);
                return true;
            }

從上面的代碼可以知道梭姓,其實(shí)就是走startSpecificActivityLocked,這根第一次從桌面喚起APP沒多大區(qū)別嫩码,只是有一點(diǎn)需要注意誉尖,那就是這種時(shí)候啟動(dòng)的Activity是有上一次的現(xiàn)場數(shù)據(jù)傳遞過得去的,因?yàn)樯洗卧谕说胶笈_(tái)的時(shí)候铸题,所有Activity界面的現(xiàn)場都是被保存了铡恕,并且傳遞到AMS中去的,那么這次的恢復(fù)啟動(dòng)就會(huì)將這些數(shù)據(jù)返回給ActivityThread丢间,再來仔細(xì)看一下performLaunchActivity里面關(guān)于恢復(fù)的特殊處理代碼:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {


    ActivityInfo aInfo = r.activityInfo;
     Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
     ...
    }
     try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...
             關(guān)鍵點(diǎn) 1 
            mInstrumentation.callActivityOnCreate(activity, r.state);
            ...
            r.activity = activity;
            r.stopped = true;
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }
            關(guān)鍵點(diǎn) 1 
            if (!r.activity.mFinished) {
                if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            if (!r.activity.mFinished) {
                activity.mCalled = false;
                mInstrumentation.callActivityOnPostCreate(activity, r.state);
            ...

}

看一下關(guān)鍵點(diǎn)1跟2探熔,先看關(guān)鍵點(diǎn)1,mInstrumentation.callActivityOnCreate會(huì)回調(diào)Actiivyt的onCreate烘挫,這個(gè)函數(shù)里面其實(shí)主要針對FragmentActivity做一些Fragment恢復(fù)的工作诀艰,ActivityClientRecord中的r.state是AMS傳給APP用來恢復(fù)現(xiàn)場的,正常啟動(dòng)的時(shí)候,這些都是null其垄。再來看關(guān)鍵點(diǎn)2 苛蒲,在r.state != null非空的時(shí)候執(zhí)行mInstrumentation.callActivityOnRestoreInstanceState,這個(gè)函數(shù)默認(rèn)主要就是針對Window做一些恢復(fù)工作捉捅,比如ViewPager恢復(fù)之前的顯示位置等撤防,也可以用來恢復(fù)用戶保存數(shù)據(jù)。

Application沒有被后臺(tái)殺死棒口,Activity被殺死的恢復(fù)

打開開發(fā)者模式”不保留活動(dòng)“寄月,就是這種場景,在上面的分析中无牵,知道漾肮,AMS主動(dòng)異常殺死Activity的時(shí)候,將AcitivityRecord的app字段置空茎毁,因此resumeTopActivityLocked同整個(gè)APP被殺死不同克懊,會(huì)走下面的分支

 final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
     ...
        
    if (next.app != null && next.app.thread != null) { 
        ...
        
    } else {
            關(guān)鍵點(diǎn) 1 只是重啟Activity,可見這里其實(shí)是知道的七蜘,進(jìn)程并沒死谭溉,
        // Whoops, need to restart this activity!
        
        startSpecificActivityLocked(next, true, true);
    }

    return true;
}

雖然不太一樣,但是同樣走startSpecificActivityLocked流程橡卤,只是不新建APP進(jìn)程扮念,其余的都是一樣的,不再講解碧库。到這里柜与,我們應(yīng)該就了解了,

  • Android是如何在預(yù)防的情況下保存場景
  • AMS如何知道APP是否被后臺(tái)殺死
  • AMS如何根據(jù)ActivityStack重建APP被殺死時(shí)的場景

到這里ActivityManagerService恢復(fù)APP場景的邏輯就應(yīng)該講完了嵌灰。再碎碎念一些問題弄匕,可能是一些面試的點(diǎn)。

  • 主動(dòng)清除最近任務(wù)跟異常殺死的區(qū)別:ActivityStack是否正常清楚
  • 恢復(fù)的時(shí)候沽瞭,為什么是倒序恢復(fù):因?yàn)檫@是ActivityStack中的HistoryRecord中棧的順序迁匠,嚴(yán)格按照AMS端來
  • 一句話概括Android后臺(tái)殺死恢復(fù)原理:Application進(jìn)程被Kill,但現(xiàn)場被AMS保存驹溃,AMS能根據(jù)保存恢復(fù)Application現(xiàn)場

Android后臺(tái)殺死系列之一:FragmentActivity及PhoneWindow后臺(tái)殺死處理機(jī)制
Android后臺(tái)殺死系列之二:ActivityManagerService與App現(xiàn)場恢復(fù)機(jī)制
Android后臺(tái)殺死系列之三:LowMemoryKiller原理(4.3-6.0)
Android后臺(tái)殺死系列之四:Binder訃告原理
Android后臺(tái)殺死系列之五:Android進(jìn)程背巧ィ活-自“裁”或者耍流氓

作者:看書的小蝸牛
原文鏈接: Android后臺(tái)殺死系列之二:ActivityManagerService與App現(xiàn)場恢復(fù)機(jī)制

僅供參考,歡迎指正

參考文檔

Android應(yīng)用程序啟動(dòng)過程源代碼分析
Android Framework架構(gòu)淺析之【近期任務(wù)】
Android Low Memory Killer介紹
Android開發(fā)之InstanceState詳解
對Android近期任務(wù)列表(Recent Applications)的簡單分析
Android——內(nèi)存管理-lowmemorykiller 機(jī)制
Android 操作系統(tǒng)的內(nèi)存回收機(jī)制
Android LowMemoryKiller原理分析
Android進(jìn)程生命周期與ADJ
Linux下/proc目錄簡介
startActivity啟動(dòng)過程分析 精
Activity銷毀流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吠架,一起剝皮案震驚了整個(gè)濱河市芙贫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌傍药,老刑警劉巖磺平,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魂仍,死亡現(xiàn)場離奇詭異,居然都是意外死亡拣挪,警方通過查閱死者的電腦和手機(jī)擦酌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菠劝,“玉大人赊舶,你說我怎么就攤上這事「险铮” “怎么了笼平?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舔痪。 經(jīng)常有香客問我寓调,道長,這世上最難降的妖魔是什么锄码? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任夺英,我火速辦了婚禮,結(jié)果婚禮上滋捶,老公的妹妹穿的比我還像新娘痛悯。我一直安慰自己,他們只是感情好重窟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布载萌。 她就那樣靜靜地躺著,像睡著了一般亲族。 火紅的嫁衣襯著肌膚如雪炒考。 梳的紋絲不亂的頭發(fā)上可缚,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天霎迫,我揣著相機(jī)與錄音,去河邊找鬼帘靡。 笑死知给,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的描姚。 我是一名探鬼主播涩赢,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼轩勘!你這毒婦竟也來了筒扒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤绊寻,失蹤者是張志新(化名)和其女友劉穎花墩,沒想到半個(gè)月后悬秉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冰蘑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年和泌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祠肥。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡武氓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仇箱,到底是詐尸還是另有隱情县恕,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布剂桥,位于F島的核電站弱睦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渊额。R本人自食惡果不足惜况木,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望旬迹。 院中可真熱鬧火惊,春花似錦、人聲如沸奔垦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椿猎。三九已至惶岭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間犯眠,已是汗流浹背按灶。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留筐咧,地道東北人鸯旁。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像量蕊,于是被迫代替她去往敵國和親铺罢。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容