Android后台杀死系列之二:ActivityManagerService与App现场恢复机制
本篇是Android后臺殺死系列的第二篇,主要講解ActivityMangerService是如何恢復被后臺殺死的進程的(基于4.3 ),在開篇?FragmentActivity及PhoneWindow后臺殺死處理機制?中,簡述了后臺殺死所引起的一些常見問題,還有Android系統(tǒng)控件對后臺殺死所做的一些兼容,以及onSaveInstance跟onRestoreInstance的作用于執(zhí)行時機,最后說了如何應對后臺殺死,但是對于被后臺殺死的進程如何恢復的并沒有講解,本篇不涉及后臺殺死,比如LowmemoryKiller機制,只講述被殺死的進程如何恢復的。假設,一個應用被后臺殺死,再次從最近的任務列表喚起App時候,系統(tǒng)是如何處理的呢?有這么幾個問題可能需要解決:
-
Android框架層(AMS)如何知道App被殺死了
-
App被殺前的場景是如何保存的
-
系統(tǒng)(AMS)如何恢復被殺的App
-
被后臺殺死的App的啟動流程跟普通的啟動有什么區(qū)別
-
Activity的恢復順序為什么是倒序恢復
系統(tǒng)(AMS)如何知道App被殺死了
首先來看第一個問題,系統(tǒng)如何知道Application被殺死了,Android使用了Linux的oomKiller機制,只是簡單的做了個變種,采用分等級的LowmemoryKiller,但這個其實是內(nèi)核層面的,LowmemoryKiller殺死進程后,不會像用戶空間發(fā)送通知,也就是說框架層的ActivityMangerService無法知道App是否被殺死,但是,只有知道App或者Activity是否被殺死,AMS(ActivityMangerService)才能正確的走喚起流程,那么AMS究竟是在什么時候知道App或者Activity被后臺殺死了呢?我們先看一下從最近的任務列表進行喚起的時候,究竟發(fā)生了什么。
從最近的任務列表或者Icon再次喚起App的流程
在系統(tǒng)源碼systemUi的包里,有個RecentActivity,這個其實就是最近的任務列表的入口,而其呈現(xiàn)界面是通過RecentsPanelView來展現(xià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;...// 關鍵點 1 如果TaskDescription沒有被主動關閉,正常關閉,ad.taskId就是>=0if (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));}... }在上面的代碼里面,有個判斷ad.taskId >= 0,如果滿足這個條件,就通過moveTaskToFront喚起APP,那么ad.taskId是如何獲取的?recent包里面有各類RecentTasksLoader,這個類就是用來加載最近任務列表的一個Loader,看一下它的源碼,主要看一下加載:
@Overrideprotected 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 itemsfinal 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);....}可以看到,其實就是通過ActivityManger的getRecentTasks向AMS請求最近的任務信息,然后通過createTaskDescription創(chuàng)建TaskDescription,這里傳遞的recentInfo.id其實就是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其實就是TaskRecode中記錄的ActivityRecord的數(shù)目,更具體的細節(jié)可以自行查看ActivityManagerService及ActivityStack,那么這里就容易解釋了,只要是存活的APP、或者被LowmemoryKiller殺死的APP,其AMS的ActivityRecord是完整保存的,這就是恢復的依據(jù)。RecentActivity獲取的數(shù)據(jù)其實就是AMS中的翻版,RecentActivity并不知道將要喚起的APP是否是存活的,只要TaskRecord告訴RecentActivity是存貨的,那么久直接走喚起流程,也就是通過ActivityManager的moveTaskToFront喚起App,至于后續(xù)的工作,就完全交給AMS來處理。現(xiàn)看一下到這里的流程圖:
整個APP被后臺殺死的情況下AMS是如何恢復現(xiàn)場的
AMS與客戶端的通信是通過Binder來進行的,并且通信是”全雙工“的,且互為客戶端跟服務器,也就說AMS向客戶端發(fā)命令的時候,AMS是客戶端,反之亦然。注意?Binder有個訃告的功能的:如果基于Binder通信的服務端(S)如果掛掉了,客戶端(C)能夠收到Binder驅(qū)動發(fā)送的一份訃告,告知客戶端Binder服務掛了,可以把Binder驅(qū)動看作是第三方不死郵政機構(gòu),專門向客戶端發(fā)偶像死亡通知。對于APP被異常殺死的情況下,這份訃告是發(fā)送給AMS的,AMS在收到通知后,就會針對APP被異常殺死的情況作出整理,這里牽扯到Binder驅(qū)動的代碼有興趣可以自己翻一下。之類直接沖訃告接受后端處理邏輯來分析,在AMS源碼中,入口其實就是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;關鍵點1 handleAppDiedLocked(app, false, true);// 如果是被后臺殺了,怎么處理關鍵點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)存緊張,這個時候就會通知其他后臺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();}}}...}先看關鍵點1:在進程被殺死后,AMS端要選擇性清理進程相關信息,清理后,再根據(jù)是不是內(nèi)存低引起的后臺殺死,決定是不是需要清理其他后臺進程。接著看handleAppDiedLocked如何清理的,這里有重建時的依據(jù):ActivityRecord不清理,但是為它設置個APP未綁定的標識
private final void handleAppDiedLocked(ProcessRecord app,boolean restarting, boolean allowRestart) {關鍵點1cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);...關鍵點2// Remove this application's activities from active lists.boolean hasVisibleActivities = mMainStack.removeHistoryRecordsForAppLocked(app);app.activities.clear();...關鍵點3if (!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);}}} }看關鍵點1,cleanUpApplicationRecordLocked,主要負責清理一些Providers,receivers,service之類的信息,并且在清理過程中根據(jù)配置的一些信息決定是否需要重建進程并啟動,關鍵點2 就是關系到喚起流程的判斷,關鍵點3,主要是被殺的進程是否是當前前臺進程,如果是,需要重建,并立即顯示:先簡單看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();}... 關鍵點1,進程是夠需要重啟,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);} ... }從關鍵點1就能知道,這里是隱藏了進程是否需要重啟的邏輯,比如一個Service設置了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;<!--關鍵點1-->if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {remove = true;} else if (r.launchCount > 2 &&remove = true;} else {//一般來講,走falseremove = false;}<!--關鍵點2-->if (remove) {...removeActivityFromHistoryLocked(r);} else {...r.app = null;...}return hasVisibleActivities; }在Activity跳轉(zhuǎn)的時候,準確的說,在stopActivity之前,會保存Activity的現(xiàn)場,這樣在AMS端r.haveState==true,也就是說,其ActivityRecord不會被從ActivityStack中移除,同時ActivityRecord的app字段被置空,這里在恢復的時候,是決定resume還是重建的關鍵。接著往下看moveTaskToFrontLocked,這個函數(shù)在ActivityStack中,主要管理ActivityRecord棧的,所有start的Activity都在ActivityStack中保留一個ActivityRecord,這個也是AMS管理Activiyt的一個依據(jù),最終moveTaskToFrontLocked會調(diào)用resumeTopActivityLocked來喚起Activity,AMS獲取即將resume的Activity信息的方式主要是通過ActivityRecord,它并不知道Activity本身是否存活,獲取之后,AMS在喚醒Activity的環(huán)節(jié)才知道App或者Activity被殺死,具體看一下resumeTopActivityLocked源碼:
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {...關鍵點1 if (next.app != null && next.app.thread != null) { ...} else {// Whoops, need to restart this activity!...startSpecificActivityLocked(next, true, true);}return true;}看關鍵點1的判斷條件,由于已經(jīng)將ActivityRecord的app字段置空,AMS就知道了這個APP或者Activity被異常殺死過,因此,就會走startSpecificActivityLocked進行重建。 其實仔細想想很簡單,對于主動調(diào)用finish的,AMS并不會清理掉ActivitRecord,在喚起APP的時候,如果AMS檢測到APP還存活,就走scheduleResumeActivity進行喚起上一個Activity,但是如果APP或者Activity被異常殺死過,那么AMS就通過startSpecificActivityLocked再次將APP重建,并且將最后的Activity重建。
APP存活,但是Activity被后臺殺死的情況下AMS是如何恢復現(xiàn)場的
還有一種可能,APP沒有被kill,但是Activity被Kill掉了,這個時候會怎么樣?首先,Activity的管理是一定通過AMS的,Activity的kill一定是是AMS操刀的,是有記錄的,嚴格來說,這種情況并不屬于后臺殺死,因為這屬于AMS正常的管理,在可控范圍,比如打開了開發(fā)者模式中的“不保留活動”,這個時候,雖然會殺死Activity,但是仍然保留了ActivitRecord,所以再喚醒,或者回退的的時候仍然有跡可循,看一下ActivityStack的Destroy回調(diào)代碼,
final boolean destroyActivityLocked(ActivityRecord r,boolean removeFromApp, boolean oomAdj, String reason) {...if (hadApp) {...boolean skipDestroy = false;try {關鍵代碼 1r.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 {關鍵代碼 2r.state = ActivityState.DESTROYED;if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity " + r);r.app = null;}} return removedFromHistory;}這里有兩個關鍵啊你單,1是告訴客戶端的AcvitityThread清除Activity,2是標記如果AMS自己非正常關閉的Activity,就將ActivityRecord的state設置為ActivityState.DESTROYED,并且清空它的ProcessRecord引用:r.app = null。這里是喚醒時候的一個重要標志,通過這里AMS就能知道Activity被自己異常關閉了,設置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 <!--這里會是否從history列表移除ActivityRecord-->if (r.state == ActivityState.DESTROYING) {cleanUpActivityLocked(r, true, false);removeActivityFromHistoryLocked(r);}}resumeTopActivityLocked(null);} finally {Binder.restoreCallingIdentity(origId);}} }看代碼關鍵點1,只有r.state == ActivityState.DESTROYING的時候,才會移除ActivityRecord,但是對于不非正常finish的Activity,其狀態(tài)是不會被設置成ActivityState.DESTROYING,是直接跳過了ActivityState.DESTROYING,被設置成了ActivityState.DESTROYED,所以不會removeActivityFromHistoryLocked,也就是保留了ActivityRecord現(xiàn)場,好像也是依靠異常來區(qū)分是否是正常的結(jié)束掉Activity。這種情況下是如何啟動Activity的呢? 通過上面兩點分析,就知道了兩個關鍵點
ActivityRecord沒有動HistoryRecord列表中移除
ActivityRecord 的ProcessRecord字段被置空,r.app = null
這樣就保證了在resumeTopActivityLocked的時候,走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就知道了這個APP或者Activity是不是被異常殺死過,從而,決定是走resume流程還是restore流程。
App被殺前的場景是如何保存的: 新Activity啟動跟舊Activity的保存
App現(xiàn)場的保存流程相對是比較簡單的,入口基本就是startActivity的時候,只要是界面的跳轉(zhuǎn)基本都牽扯到Activity的切換跟當前Activity場景的保存:先畫個簡單的圖形,開偏里面講FragmentActivity的時候,簡單說了一些onSaveInstance的執(zhí)行時機,這里詳細看一下AMS是如何管理這些跳轉(zhuǎn)以及場景保存的,模擬場景:Activity A 啟動Activity B的時候,這個時候A不可見,可能會被銷毀,需要保存A的現(xiàn)場,這個流程是什么樣的:簡述如下
-
ActivityA startActivity ActivityB
-
ActivityA pause
-
ActivityB create
-
ActivityB start
-
ActivityB resume
-
ActivityA onSaveInstance
-
ActivityA stop
流程大概是如下樣子:
現(xiàn)在我們通過源碼一步一步跟一下,看看AMS在新Activity啟動跟舊Activity的保存的時候,到底做了什么:跳過簡單的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啟動新的APP,或者新Activity,這里只看簡單的,至于從桌面啟動App的流程,可以去參考更詳細的文章,比如老羅的startActivity流程,大概就是新建ActivityRecord,ProcessRecord之類,并加入AMS中相應的堆棧等,resumeTopActivityLocked是界面切換的統(tǒng)一入口,第一次進來的時候,由于ActivityA還在沒有pause,因此需要先暫停ActivityA,這些完成后,
ActivityStack
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) { ...<!--必須將當前Resume的Activity設置為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;}.... }其實這里就是暫停ActivityA,AMS通過Binder告訴ActivityThread需要暫停的ActivityA,ActivityThread完成后再通過Binder通知AMS,AMS會開始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消息之后,就會喚起ActivityB,入口還是resumeTopActivityLocked,喚醒B,之后還會A給進一步stop掉,這個時候就牽扯到現(xiàn)場的保存,
ActivityStack
private final void completePauseLocked() {if (!mService.isSleeping()) {resumeTopActivityLocked(prev);} else {... }ActivityB如何啟動的,本文不關心,只看ActivityA如何保存現(xiàn)場的,ActivityB起來后,會通過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動作完成,在通知的時候,還會將保存的現(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不僅啟動了新的Activity,同時也將上一個Activity的現(xiàn)場進行了保存,及時由于種種原因上一個Actiivity被殺死,在回退,或者重新喚醒的過程中AMS也能知道如何喚起Activiyt,并恢復。
現(xiàn)在解決兩個問題,1、如何保存現(xiàn)場,2、AMS怎么判斷知道APP或者Activity是否被異常殺死,那么就剩下最后一個問題了,AMS如何恢復被異常殺死的APP或者Activity呢。
整個Application被后臺殺死情況下的恢復邏輯
其實在講解AMS怎么判斷知道APP或者Activity是否被異常殺死的時候,就已經(jīng)涉及了恢復的邏輯,也知道了一旦AMS知道了APP被后臺殺死了,那就不是正常的resuem流程了,而是要重新laucher,先來看一下整個APP被干掉的會怎么處理,看resumeTopActivityLocked部分,從上面的分析已知,這種場景下,會因為Binder通信拋異常走異常分支,如下:
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!這里是知道整個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;}從上面的代碼可以知道,其實就是走startSpecificActivityLocked,這根第一次從桌面喚起APP沒多大區(qū)別,只是有一點需要注意,那就是這種時候啟動的Activity是有上一次的現(xiàn)場數(shù)據(jù)傳遞過得去的,因為上次在退到后臺的時候,所有Activity界面的現(xiàn)場都是被保存了,并且傳遞到AMS中去的,那么這次的恢復啟動就會將這些數(shù)據(jù)返回給ActivityThread,再來仔細看一下performLaunchActivity里面關于恢復的特殊處理代碼:
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);...關鍵點 1 mInstrumentation.callActivityOnCreate(activity, r.state);...r.activity = activity;r.stopped = true;if (!r.activity.mFinished) {activity.performStart();r.stopped = false;}關鍵點 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);...}看一下關鍵點1跟2,先看關鍵點1,mInstrumentation.callActivityOnCreate會回調(diào)Actiivyt的onCreate,這個函數(shù)里面其實主要針對FragmentActivity做一些Fragment恢復的工作,ActivityClientRecord中的r.state是AMS傳給APP用來恢復現(xiàn)場的,正常啟動的時候,這些都是null。再來看關鍵點2 ,在r.state != null非空的時候執(zhí)行mInstrumentation.callActivityOnRestoreInstanceState,這個函數(shù)默認主要就是針對Window做一些恢復工作,比如ViewPager恢復之前的顯示位置等,也可以用來恢復用戶保存數(shù)據(jù)。
Application沒有被后臺殺死,Activity被殺死的恢復
打開開發(fā)者模式”不保留活動“,就是這種場景,在上面的分析中,知道,AMS主動異常殺死Activity的時候,將AcitivityRecord的app字段置空,因此resumeTopActivityLocked同整個APP被殺死不同,會走下面的分支
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {...if (next.app != null && next.app.thread != null) { ...} else {關鍵點 1 只是重啟Activity,可見這里其實是知道的,進程并沒死,// Whoops, need to restart this activity!startSpecificActivityLocked(next, true, true);}return true; }雖然不太一樣,但是同樣走startSpecificActivityLocked流程,只是不新建APP進程,其余的都是一樣的,不再講解。到這里,我們應該就了解了,
-
Android是如何在預防的情況下保存場景
-
AMS如何知道APP是否被后臺殺死
-
AMS如何根據(jù)ActivityStack重建APP被殺死時的場景
到這里ActivityManagerService恢復APP場景的邏輯就應該講完了。再碎碎念一些問題,可能是一些面試的點。
-
主動清除最近任務跟異常殺死的區(qū)別:ActivityStack是否正常清楚
-
恢復的時候,為什么是倒序恢復:因為這是ActivityStack中的HistoryRecord中棧的順序,嚴格按照AMS端來
-
一句話概括Android后臺殺死恢復原理:Application進程被Kill,但現(xiàn)場被AMS保存,AMS能根據(jù)保存恢復Application現(xiàn)場
僅供參考,歡迎指正
參考文檔
Android應用程序啟動過程源代碼分析?
Android Framework架構(gòu)淺析之【近期任務】?
Android Low Memory Killer介紹?
Android開發(fā)之InstanceState詳解?
對Android近期任務列表(Recent Applications)的簡單分析?
Android——內(nèi)存管理-lowmemorykiller 機制?
Android 操作系統(tǒng)的內(nèi)存回收機制?
Android LowMemoryKiller原理分析?
Android進程生命周期與ADJ?
Linux下/proc目錄簡介?
startActivity啟動過程分析 精
Activity銷毀流程
原文地址:https://segmentfault.com/a/1190000008085504
總結(jié)
以上是生活随笔為你收集整理的Android后台杀死系列之二:ActivityManagerService与App现场恢复机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一种实现(无须root)手机截屏方案
- 下一篇: Android后台杀死系列之三:LowM