Android后台杀死系列之二:ActivityManagerService与App现场恢复机制

移动开发 Android
本篇是Android后台杀死系列的第二篇,主要讲解ActivityMangerService是如何恢复被后台杀死的进程的(基于4.3 ),在开篇FragmentActivity及PhoneWindow后台杀死处理机制 中,简述了后台杀死所引起的一些常见问题,还有Android系统控件对后台杀死所做的一些兼容,以及onSaveInstance跟onRestoreInstance的作用于执行时机,最后说了如何应对后台杀死,但是对于被后台杀死的进程如何恢复的并没有讲解,本篇不涉及后台杀死,比如LowmemoryKiller机制,只讲述被杀死的进程如何恢复的。

  

本篇是Android后台杀死系列的第二篇,主要讲解ActivityMangerService是如何恢复被后台杀死的进程的(基于4.3 ),在开篇FragmentActivity及PhoneWindow后台杀死处理机制 中,简述了后台杀死所引起的一些常见问题,还有Android系统控件对后台杀死所做的一些兼容,以及onSaveInstance跟onRestoreInstance的作用于执行时机,最后说了如何应对后台杀死,但是对于被后台杀死的进程如何恢复的并没有讲解,本篇不涉及后台杀死,比如LowmemoryKiller机制,只讲述被杀死的进程如何恢复的。假设,一个应用被后台杀死,再次从最近的任务列表唤起App时候,系统是如何处理的呢?有这么几个问题可能需要解决:

  • Android框架层(AMS)如何知道App被杀死了
  • App被杀前的场景是如何保存的
  • 系统(AMS)如何恢复被杀的App
  • 被后台杀死的App的启动流程跟普通的启动有什么区别
  • Activity的恢复顺序为什么是倒序恢复

系统(AMS)如何知道App被杀死了

首先来看第一个问题,系统如何知道Application被杀死了,Android使用了Linux的oomKiller机制,只是简单的做了个变种,采用分等级的LowmemoryKiller,但这个其实是内核层面的,LowmemoryKiller杀死进程后,不会像用户空间发送通知,也就是说框架层的ActivityMangerService无法知道App是否被杀死,但是,只有知道App或者Activity是否被杀死,AMS(ActivityMangerService)才能正确的走唤起流程,那么AMS究竟是在什么时候知道App或者Activity被后台杀死了呢?我们先看一下从最近的任务列表进行唤起的时候,究竟发生了什么。

从最近的任务列表或者Icon再次唤起App的流程

在系统源码systemUi的包里,有个RecentActivity,这个其实就是最近的任务列表的入口,而其呈现界面是通过RecentsPanelView来展现的,点击最近的App其执行代码如下:

  1. public void handleOnClick(View view) { 
  2.     ViewHolder holder = (ViewHolder)view.getTag(); 
  3.     TaskDescription ad = holder.taskDescription; 
  4.     final Context context = view.getContext(); 
  5.     final ActivityManager am = (ActivityManager) 
  6.             context.getSystemService(Context.ACTIVITY_SERVICE); 
  7.     Bitmap bm = holder.thumbnailViewImageBitmap; 
  8.     ... 
  9.     // 关键点 1  如果TaskDescription没有被主动关闭,正常关闭,ad.taskId就是>=0 
  10.     if (ad.taskId >= 0) { 
  11.         // This is an active task; it should just go to the foreground. 
  12.         am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME, 
  13.                 opts); 
  14.     } else { 
  15.         Intent intent = ad.intent; 
  16.         intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 
  17.                 | Intent.FLAG_ACTIVITY_TASK_ON_HOME 
  18.                 | Intent.FLAG_ACTIVITY_NEW_TASK); 
  19.         try { 
  20.             context.startActivityAsUser(intent, opts, 
  21.                     new UserHandle(UserHandle.USER_CURRENT)); 
  22.         }... 
  23.  

在上面的代码里面,有个判断ad.taskId >= 0,如果满足这个条件,就通过moveTaskToFront唤起APP,那么ad.taskId是如何获取的?recent包里面有各类RecentTasksLoader,这个类就是用来加载最近任务列表的一个Loader,看一下它的源码,主要看一下加载:

  1. @Override 
  2.        protected Void doInBackground(Void... params) { 
  3.            // We load in two stages: first, we update progress with just the first screenful 
  4.            // of items. Then, we update with the rest of the items 
  5.            final int origPri = Process.getThreadPriority(Process.myTid()); 
  6.            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 
  7.            final PackageManager pm = mContext.getPackageManager(); 
  8.            final ActivityManager am = (ActivityManager) 
  9.            mContext.getSystemService(Context.ACTIVITY_SERVICE); 
  10.  
  11.            final List<ActivityManager.RecentTaskInfo> recentTasks = 
  12.                    am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE); 
  13.              
  14.            .... 
  15.                TaskDescription item = createTaskDescription(recentInfo.id, 
  16.                        recentInfo.persistentId, recentInfo.baseIntent, 
  17.                        recentInfo.origActivity, recentInfo.description); 
  18.            .... 
  19.            }   

可以看到,其实就是通过ActivityManger的getRecentTasks向AMS请求最近的任务信息,然后通过createTaskDescription创建TaskDescription,这里传递的recentInfo.id其实就是TaskDescription的taskId,来看一下它的意义:

  1. public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, 
  2.         int flags, int userId) { 
  3.         ...            
  4.         IPackageManager pm = AppGlobals.getPackageManager(); 
  5.  
  6.         final int N = mRecentTasks.size(); 
  7.         ... 
  8.         for (int i=0; i<N && maxNum > 0; i++) { 
  9.             TaskRecord tr = mRecentTasks.get(i); 
  10.             if (i == 0 
  11.                     || ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0) 
  12.                     || (tr.intent == null
  13.                     || ((tr.intent.getFlags() 
  14.                             &Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0)) { 
  15.                 ActivityManager.RecentTaskInfo rti 
  16.                         = new ActivityManager.RecentTaskInfo(); 
  17.                 rti.id = tr.numActivities > 0 ? tr.taskId : -1; 
  18.                 rti.persistentId = tr.taskId; 
  19.                 rti.baseIntent = new Intent( 
  20.                         tr.intent != null ? tr.intent : tr.affinityIntent); 
  21.                 if (!detailed) { 
  22.                     rti.baseIntent.replaceExtras((Bundle)null); 
  23.                 }  

可以看出RecentTaskInfo的id是由TaskRecord决定的,如果TaskRecord中numActivities > 0就去TaskRecord的Id,否则就取-1,这里的numActivities其实就是TaskRecode中记录的ActivityRecord的数目,更具体的细节可以自行查看ActivityManagerService及ActivityStack,那么这里就容易解释了,只要是存活的APP、或者被LowmemoryKiller杀死的APP,其AMS的ActivityRecord是完整保存的,这就是恢复的依据。RecentActivity获取的数据其实就是AMS中的翻版,RecentActivity并不知道将要唤起的APP是否是存活的,只要TaskRecord告诉RecentActivity是存货的,那么久直接走唤起流程,也就是通过ActivityManager的moveTaskToFront唤起App,至于后续的工作,就完全交给AMS来处理。现看一下到这里的流程图: 

 

 

 

在唤起App的时候AMS侦测App或者Activity是否被异常杀死

接着往下看moveTaskToFrontLocked,这个函数在ActivityStack中,ActivityStack主要用来管理ActivityRecord栈的,所有start的Activity都在ActivityStack中保留一个ActivityRecord,这个也是AMS管理Activity的一个依据,ActivityStack最终moveTaskToFrontLocked会调用resumeTopActivityLocked来唤起Activity,AMS获取即将resume的Activity信息的方式主要是通过ActivityRecord,它并不知道Activity本身是否存活,获取之后,AMS知道唤醒Activity的环节才知道App或者Activity被杀死,具体看一下resumeTopActivityLocked源码:

  1. final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) { 
  2.    
  3.      // This activity is now becoming visible. 
  4.         mService.mWindowManager.setAppVisibility(next.appToken, true); 
  5.                 
  6.      ....    恢复逻辑   
  7.     if (next.app != null && next.app.thread != null) { 
  8.       // 正常恢复 
  9.         try { 
  10.             // Deliver all pending results. 
  11.             ArrayList a = next.results; 
  12.             if (a != null) { 
  13.                 final int N = a.size(); 
  14.                 if (!next.finishing && N > 0) { 
  15.                     next.app.thread.scheduleSendResult(next.appToken, a); 
  16.                 } 
  17.             } 
  18.             ... 
  19.             next.app.thread.scheduleResumeActivity(next.appToken, 
  20.                     mService.isNextTransitionForward());  
  21.             ... 
  22.         } catch (Exception e) { 
  23.             // Whoops, need to restart this activity! 
  24.             // 这里需要重启,难道被后台杀死,走的是异常分支吗???? 异常杀死 
  25.             if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to " 
  26.                     + lastState + ": " + next); 
  27.             next.state = lastState; 
  28.             mResumedActivity = lastResumedActivity; 
  29.             <!--确实这里是因为进程挂掉了--> 
  30.             Slog.i(TAG, "Restarting because process died: " + next); 
  31.              。。。 
  32.             startSpecificActivityLocked(nexttruefalse); 
  33.             return true
  34.         } 
  35.         ... 
  36.         }  

由于没有主动调用finish的,所以AMS并不会清理掉ActivityRecord与TaskRecord ,因此resume的时候走的就是上面的分支,可以这里会调用next.app.thread.scheduleSendResult或者next.app.thread.scheduleResumeActivity进行唤起上一个Activity,但是如果APP或者Activity被异常杀死,那么唤起的操作一定是失败,会抛出异常,首先假设APP整个被杀死,那么APP端同AMS通信的Binder线程也不复存在,这个时候通过Binder进行通信就会抛出RemoteException,如此,就会走下面的catch部分,通过startSpecificActivityLocked再次将APP重建,并且将最后的Activity重建,其实你可以本地利用AIDL写一个C/S通信,在将一端关闭,然后用另一端访问,就会抛出RemoteException异常,如下图: 

 

 

 

还有一种可能,APP没有被kill,但是Activity被Kill掉了,这个时候会怎么样?首先,Activity的管理是一定通过AMS的,Activity的kill一定是是AMS操刀的,是有记录的,严格来说,这种情况并不属于后台杀死,因为这属于AMS正常的管理,在可控范围,比如打开了开发者模式中的“不保留活动”,这个时候,虽然会杀死Activity,但是仍然保留了ActivitRecord,所以再唤醒,或者回退的的时候仍然有迹可循,看一下ActivityStack的Destroy回调代码,

  1. final boolean destroyActivityLocked(ActivityRecord r, 
  2.             boolean removeFromApp, boolean oomAdj, String reason) { 
  3.         ... 
  4.         if (hadApp) { 
  5.           ... 
  6.            boolean skipDestroy = false
  7.             try { 
  8.              关键代码 1 
  9.                 r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing, 
  10.                         r.configChangeFlags); 
  11.              ... 
  12.             if (r.finishing && !skipDestroy) { 
  13.                 if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYING: " + r 
  14.                         + " (destroy requested)"); 
  15.                 r.state = ActivityState.DESTROYING; 
  16.                 Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG); 
  17.                 msg.obj = r; 
  18.                 mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT); 
  19.             } else { 
  20.           关键代码 2 
  21.                 r.state = ActivityState.DESTROYED; 
  22.                 if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity " + r); 
  23.                 r.app = null
  24.             } 
  25.         }  
  26.         return removedFromHistory; 
  27.     }   

这里有两个关键啊你单,1是告诉客户端的AcvitityThread清除Activity,2是标记如果AMS自己非正常关闭的Activity,就将ActivityRecord的state设置为ActivityState.DESTROYED,并且清空它的ProcessRecord引用:r.app = null。这里是唤醒时候的一个重要标志,通过这里AMS就能知道Activity被自己异常关闭了,设置ActivityState.DESTROYED是为了让避免后面的清空逻辑。

  1. final void activityDestroyed(IBinder token) { 
  2.     synchronized (mService) { 
  3.         final long origId = Binder.clearCallingIdentity(); 
  4.         try { 
  5.             ActivityRecord r = ActivityRecord.forToken(token); 
  6.             if (r != null) { 
  7.                 mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r); 
  8.             } 
  9.            int index = indexOfActivityLocked(r); 
  10.             if (index >= 0) { 
  11.             1  <!--这里会是否从history列表移除ActivityRecord--> 
  12.                 if (r.state == ActivityState.DESTROYING) { 
  13.                     cleanUpActivityLocked(r, truefalse); 
  14.                     removeActivityFromHistoryLocked(r); 
  15.                 } 
  16.             } 
  17.             resumeTopActivityLocked(null); 
  18.         } finally { 
  19.             Binder.restoreCallingIdentity(origId); 
  20.         } 
  21.     } 
  22.  

看代码关键点1,只有r.state == ActivityState.DESTROYING的时候,才会移除ActivityRecord,但是对于不非正常finish的Activity,其状态是不会被设置成ActivityState.DESTROYING,是直接跳过了ActivityState.DESTROYING,被设置成了ActivityState.DESTROYED,所以不会removeActivityFromHistoryLocked,也就是保留了ActivityRecord现场,好像也是依靠异常来区分是否是正常的结束掉Activity。这种情况下是如何启动Activity的呢? 通过上面两点分析,就知道了两个关键点

  1. ActivityRecord没有动HistoryRecord列表中移除
  2. ActivityRecord 的ProcessRecord字段被置空,r.app = null

这样就保证了在resumeTopActivityLocked的时候,走startSpecificActivityLocked分支

  1. final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) { 
  2.           ... 
  3.               
  4.         if (next.app != null && next.app.thread != null) {  
  5.         ... 
  6.          
  7.         } else { 
  8.             // Whoops, need to restart this activity! 
  9.           ... 
  10.             startSpecificActivityLocked(nexttruetrue); 
  11.         } 
  12.  
  13.         return true
  14.     }  

到这里,AMS就知道了这个APP或者Activity是不是被异常杀死过,从而,决定是走resume流程还是restore流程。

App被杀前的场景是如何保存的: 新Activity启动跟旧Activity的保存

App现场的保存流程相对是比较简单的,入口基本就是startActivity的时候,只要是界面的跳转基本都牵扯到Activity的切换跟当前Activity场景的保存:先画个简单的图形,开偏里面讲FragmentActivity的时候,简单说了一些onSaveInstance的执行时机,这里详细看一下AMS是如何管理这些跳转以及场景保存的,模拟场景:Activity A 启动Activity B的时候,这个时候A不可见,可能会被销毁,需要保存A的现场,这个流程是什么样的:简述如下

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

流程大概是如下样子: 

 

 

 

现在我们通过源码一步一步跟一下,看看AMS在新Activity启动跟旧Activity的保存的时候,到底做了什么:跳过简单的startActivity,直接去AMS中去看

ActivityManagerService

  1. public final int startActivityAsUser(IApplicationThread caller, String callingPackage, 
  2.         Intent intent, String resolvedType, IBinder resultTo, 
  3.         String resultWho, int requestCode, int startFlags, 
  4.         String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) { 
  5.     enforceNotIsolatedCaller("startActivity"); 
  6.      ... 
  7.     return mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, 
  8.             resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, 
  9.             nullnull, options, userId); 
  10.  

ActivityStack

  1. final int startActivityMayWait(IApplicationThread caller, int callingUid, 
  2.                    
  3.         int res = startActivityLocked(caller, intent, resolvedType, 
  4.                 aInfo, resultTo, resultWho, requestCode, callingPid, callingUid, 
  5.                 callingPackage, startFlags, options, componentSpecified, null); 
  6.          
  7.      。。。 
  8. }   

这里通过startActivityMayWait启动新的APP,或者新Activity,这里只看简单的,至于从桌面启动App的流程,可以去参考更详细的文章,比如老罗的startActivity流程,大概就是新建ActivityRecord,ProcessRecord之类,并加入AMS中相应的堆栈等,resumeTopActivityLocked是界面切换的统一入口,第一次进来的时候,由于ActivityA还在没有pause,因此需要先暂停ActivityA,这些完成后,

ActivityStack

  1. final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {        
  2.    ... 
  3.    <!--必须将当前Resume的Activity设置为pause 然后stop才能继续--> 
  4.  // We need to start pausing the current activity so the top one 
  5.   // can be resumed... 
  6.   if (mResumedActivity != null) {             
  7.       if (next.app != null && next.app.thread != null) { 
  8.            
  9.           mService.updateLruProcessLocked(next.app, false); 
  10.       } 
  11.       startPausingLocked(userLeaving, false); 
  12.       return true
  13.       } 
  14.       ....  

其实这里就是暂停ActivityA,AMS通过Binder告诉ActivityThread需要暂停的ActivityA,ActivityThread完成后再通过Binder通知AMS,AMS会开始resume ActivityB,

  1. private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) { 
  2.  
  3.     if (prev.app != null && prev.app.thread != null) { 
  4.        ... 
  5.         try { 
  6.             prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing, 
  7.                     userLeaving, prev.configChangeFlags);  

ActivityThread

  1. private void handlePauseActivity(IBinder token, boolean finished, 
  2.             boolean userLeaving, int configChanges) { 
  3.         ActivityClientRecord r = mActivities.get(token); 
  4.         if (r != null) { 
  5.             ... 
  6.             performPauseActivity(token, finished, r.isPreHoneycomb()); 
  7.             ... 
  8.             // Tell the activity manager we have paused. 
  9.             try { 
  10.                 ActivityManagerNative.getDefault().activityPaused(token); 
  11.             } catch (RemoteException ex) { 
  12.             } 
  13.         } 
  14.     }  

AMS收到ActivityA发送过来的pause消息之后,就会唤起ActivityB,入口还是resumeTopActivityLocked,唤醒B,之后还会A给进一步stop掉,这个时候就牵扯到现场的保存,

ActivityStack

  1. private final void completePauseLocked() { 
  2.    
  3.    if (!mService.isSleeping()) { 
  4.        resumeTopActivityLocked(prev); 
  5.    } else { 
  6.     
  7.   ... 
  8.     

ActivityB如何启动的,本文不关心,只看ActivityA如何保存现场的,ActivityB起来后,会通过ActivityStack的stopActivityLocked去stop ActivityA,

  1. private final void stopActivityLocked(ActivityRecord r) { 
  2.        ... 
  3.         if (mMainStack) { 
  4.               
  5.             r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags); 
  6.         ... 
  7.        }  

回看APP端,看一下ActivityThread中的调用:首先通过callActivityOnSaveInstanceState,将现场保存到Bundle中去,

  1. private void performStopActivityInner(ActivityClientRecord r, 
  2.         StopInfo info, boolean keepShown, boolean saveState) { 
  3.        ... 
  4.         // Next have the activity save its current state and managed dialogs... 
  5.         if (!r.activity.mFinished && saveState) { 
  6.             if (r.state == null) { 
  7.                 state = new Bundle(); 
  8.                 state.setAllowFds(false); 
  9.                 mInstrumentation.callActivityOnSaveInstanceState(r.activity, state); 
  10.                 r.state = state; 
  11.          。。。 
  12.          }  

之后,通过ActivityManagerNative.getDefault().activityStopped,通知AMS Stop动作完成,在通知的时候,还会将保存的现场数据带过去。

  1. private static class StopInfo implements Runnable { 
  2.     ActivityClientRecord activity; 
  3.     Bundle state; 
  4.     Bitmap thumbnail; 
  5.     CharSequence description; 
  6.  
  7.     @Override public void run() { 
  8.         // Tell activity manager we have been stopped. 
  9.         try { 
  10.  
  11.             ActivityManagerNative.getDefault().activityStopped( 
  12.                 activity.token, state, thumbnail, description); 
  13.         } catch (RemoteException ex) { 
  14.         } 
  15.     } 
  16.  

通过上面流程,AMS不仅启动了新的Activity,同时也将上一个Activity的现场进行了保存,及时由于种种原因上一个Actiivity被杀死,在回退,或者重新唤醒的过程中AMS也能知道如何唤起Activiyt,并恢复。

现在解决两个问题,1、如何保存现场,2、AMS怎么判断知道APP或者Activity是否被异常杀死,那么就剩下最后一个问题了,AMS如何恢复被异常杀死的APP或者Activity呢。

整个Application被后台杀死情况下的恢复逻辑

其实在讲解AMS怎么判断知道APP或者Activity是否被异常杀死的时候,就已经涉及了恢复的逻辑,也知道了一旦AMS知道了APP被后台杀死了,那就不是正常的resuem流程了,而是要重新laucher,先来看一下整个APP被干掉的会怎么处理,看resumeTopActivityLocked部分,从上面的分析已知,这种场景下,会因为Binder通信抛异常走异常分支,如下:

  1. final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) { 
  2.   .... 
  3.   if (next.app != null && next.app.thread != null) { 
  4.             if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next); 
  5.             ...             
  6.             try { 
  7.              ... 
  8.             } catch (Exception e) { 
  9.                 // Whoops, need to restart this activity! 
  10.                 这里是知道整个app被杀死的 
  11.                 Slog.i(TAG, "Restarting because process died: " + next); 
  12.                 next.state = lastState; 
  13.                 mResumedActivity = lastResumedActivity; 
  14.                 Slog.i(TAG, "Restarting because process died: " + next); 
  15.                
  16.                 startSpecificActivityLocked(nexttruefalse); 
  17.                 return true
  18.             }  

从上面的代码可以知道,其实就是走startSpecificActivityLocked,这根第一次从桌面唤起APP没多大区别,只是有一点需要注意,那就是这种时候启动的Activity是有上一次的现场数据传递过得去的,因为上次在退到后台的时候,所有Activity界面的现场都是被保存了,并且传递到AMS中去的,那么这次的恢复启动就会将这些数据返回给ActivityThread,再来仔细看一下performLaunchActivity里面关于恢复的特殊处理代码:

  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { 
  2. ActivityInfo aInfo = r.activityInfo; 
  3.      Activity activity = null
  4.     try { 
  5.         java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); 
  6.         activity = mInstrumentation.newActivity( 
  7.                 cl, component.getClassName(), r.intent); 
  8.         StrictMode.incrementExpectedActivityCount(activity.getClass()); 
  9.         r.intent.setExtrasClassLoader(cl); 
  10.         if (r.state != null) { 
  11.             r.state.setClassLoader(cl); 
  12.         } 
  13.     } catch (Exception e) { 
  14.      ... 
  15.     } 
  16.      try { 
  17.         Application app = r.packageInfo.makeApplication(false, mInstrumentation); 
  18.             ... 
  19.              关键点 1  
  20.             mInstrumentation.callActivityOnCreate(activity, r.state); 
  21.             ... 
  22.             r.activity = activity; 
  23.             r.stopped = true
  24.             if (!r.activity.mFinished) { 
  25.                 activity.performStart(); 
  26.                 r.stopped = false
  27.             } 
  28.             关键点 1  
  29.             if (!r.activity.mFinished) { 
  30.                 if (r.state != null) { 
  31.                     mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); 
  32.                 } 
  33.             } 
  34.             if (!r.activity.mFinished) { 
  35.                 activity.mCalled = false
  36.                 mInstrumentation.callActivityOnPostCreate(activity, r.state); 
  37.             ... 
  38.  
  39. } 

看一下关键点1跟2,先看关键点1,mInstrumentation.callActivityOnCreate会回调Actiivyt的onCreate,这个函数里面其实主要针对FragmentActivity做一些Fragment恢复的工作,ActivityClientRecord中的r.state是AMS传给APP用来恢复现场的,正常启动的时候,这些都是null。再来看关键点2 ,在r.state != null非空的时候执行mInstrumentation.callActivityOnRestoreInstanceState,这个函数默认主要就是针对Window做一些恢复工作,比如ViewPager恢复之前的显示位置等,也可以用来恢复用户保存数据。

Application没有被后台杀死,Activity被杀死的恢复

打开开发者模式”不保留活动“,就是这种场景,在上面的分析中,知道,AMS主动异常杀死Activity的时候,将AcitivityRecord的app字段置空,因此resumeTopActivityLocked同整个APP被杀死不同,会走下面的分支

  1. final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) { 
  2.      ... 
  3.          
  4.     if (next.app != null && next.app.thread != null) {  
  5.         ... 
  6.          
  7.     } else { 
  8.             关键点 1 只是重启Activity,可见这里其实是知道的,进程并没死, 
  9.         // Whoops, need to restart this activity! 
  10.          
  11.         startSpecificActivityLocked(nexttruetrue); 
  12.     } 
  13.  
  14.     return true
  15.  

虽然不太一样,但是同样走startSpecificActivityLocked流程,只是不新建APP进程,其余的都是一样的,不再讲解。到这里,我们应该就了解了,

  • Android是如何在预防的情况下保存场景
  • AMS如何知道APP是否被后台杀死
  • AMS如何根据ActivityStack重建APP被杀死时的场景

到这里ActivityManagerService恢复APP场景的逻辑就应该讲完了。再碎碎念一些问题,可能是一些面试的点。

  • 主动清除最近任务跟异常杀死的区别:ActivityStack是否正常清楚
  • 恢复的时候,为什么是倒序恢复:因为这是ActivityStack中的HistoryRecord中栈的顺序,严格按照AMS端来
  • 一句话概括Android后台杀死恢复原理:Application进程被Kill,但现场被AMS保存,AMS能根据保存恢复Application现场
责任编辑:庞桂玉 来源: segmentfault
相关推荐

2017-01-12 14:21:25

AndroidFragmentActFragment

2010-05-11 14:37:56

MySQL 备份

2010-05-21 18:15:41

MySQL 备份

2010-11-04 09:43:15

虚拟备份

2019-09-11 11:38:30

MySQLInnoDB数据库

2014-06-23 17:04:25

2019-09-11 09:37:17

数据库MySQL系统

2022-03-04 15:43:36

文件管理模块Harmony鸿蒙

2011-11-28 12:55:37

JavaJVM

2023-12-25 09:26:51

监控系统工具

2023-08-07 06:39:03

网络传输

2013-05-20 17:04:09

2013-03-04 13:27:43

2015-05-06 10:16:59

2018-04-17 14:20:45

物联网发展趋势互联网

2022-02-17 19:47:03

安卓ANR故障

2011-10-17 15:03:48

2011-11-17 15:28:02

2009-09-04 13:09:06

IT职业规划喻勇

2021-11-08 15:06:15

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号