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

本篇是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(next, true, false); 
  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, true, false); 
  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(next, true, true); 
  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.             null, null, 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(next, true, false); 
  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.  

看一下关键点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(next, true, true); 
  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现场

本文作者:佚名

来源:51CTO

时间: 2024-09-18 15:05:16

Android后台杀死系列之二:ActivityManagerService与App现场恢复机制的相关文章

Android后台杀死系列之一:FragmentActivity及PhoneWindow后台杀死处理机制

App在后台久置后,再次从桌面或最近的任务列表唤醒时经常会发生崩溃,这往往是App在后台被系统杀死,再次恢复的时候遇到了问题,而在使用FragmentActivity+Fragment的时候会更加频繁.比如,如果Fragment没有提供默认构造方法,就会在重建的时候因为反射创建Fragment失败而崩溃,再比如,在onCreate里面new 一个FragmentDialog,并且show,被后台杀死后,再次唤醒的时候,就会show两个对话框,这是为什么?其实这就涉及了后台杀死及恢复的机制,其中涉

摄像头-Android 后台静静地拍摄

问题描述 Android 后台静静地拍摄 请问各位兄台,Android如何实现后台静静地使用前置摄像头拍照?就像这个程序那样http://pan.baidu.com/s/1sjKJJ8h 解决方案 你把那一个透明的像素设置成悬浮窗,就像手机360卫士那样的,悬浮在上层.这样不管切换什么程序,你也可以拍摄了. 解决方案二: 做成service然后一个像素 解决方案三: 把view做成一个像素就可以了 解决方案四: "把view做成一个像素就可以了"这个我明白,但是我要做到,在别的程序运行

刷应用-android 后台订单看见有换行特殊字,如何处理

问题描述 android 后台订单看见有换行特殊字,如何处理 android划积分,兑换应用,当用户通过支付宝提交订单时,从服务器后台可以看到停交的订单姓名后面带有换行字符,希望大神帮我分析一下,关键是我前端对输入姓名时已经设置了,是不会出现换行的,是有人恶意刷我们的应用吗? 解决方案 应该不是,怀疑是http传输时加的换行字符,或者你们处理程序不严谨,造成多了换行 解决方案二: 一个输入限制,另一个就是对数据处理 trim应该可以帮到你

Android 使用魅族系列进行测试是发现的问题 求大神告知

问题描述 Android 使用魅族系列进行测试是发现的问题 求大神告知 1C 就是这俩处错误 只是说明原因造成的 本人对于机型适配方面了解的比较少 希望大神指点 解决方案 少了一个文件,libGlobal.so 解决方案二: 楼上说的对,你打包时是不是把那个libGlobal.so文件漏了,还有/proc/mtprof/status 文件不存在,这可能就是深度修改的系统的原因吧,miui也没有这文件,你看看原生安卓有没有,没有的话,检查是不是写错路径了 解决方案三: 检查下libGlobal.s

C#微信公众号开发系列教程二(新手接入指南)

原文:C#微信公众号开发系列教程二(新手接入指南) 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可直接跳过,也欢迎大神吐槽. 目录 C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南)   微信公众平台消息接口的工作原理大概可以这样理解:从用户端到公众号端一个流程是这样的,用户发送消息到微信服务器,微信服务器将接收到的消息post到用

Android后台保活实践总结:即时通讯应用无法根治的“顽疾”

前言 Android进程和Service的保活,是困扰Android开发人员的一大顽疾.因涉及到省电和内存管理策略,各厂商基于自家的理解,在自已ROOM发布于都对标准Android发行版作为或多或少的改动,使得应用层程序在处理进程和Service保活问题上变的异常复杂,且很难兼容,因为说不定哪款手机或者哪个版本的省电策略发生改变,那么随之而来的就是进程和Service保活的差异. 在应用场景上,由于即时通讯应用(包括IM聊天应用.消息推送服务等)为了保证消息的全时.实时送达能力,必须要实现进程或

android后台运行,由于内存紧张把我的应用内存回收了或是kill了怎么恢复?

问题描述 android后台运行,由于内存紧张把我的应用内存回收了或是kill了怎么恢复? android后台运行,由于内存紧张把我的应用内存回收了或是kill了怎么恢复? 解决方案 kill了进程就没有了,所有没有保存的数据,内存中的数据就没有了. 你应该在本地用sqlite保存这些数据,或者联网上传数据.下次启动读入,恢复程序. 解决方案二: android应用程序,执行了就占用内在.如果使用内存的优化,清除也就是将应用程序删除了,终止运行. 设计比较好的应用程序,在退出程序时,将重要的数据

按键-android 后台如何检测音量键消息?

问题描述 android 后台如何检测音量键消息? 我们有个APK需求,需要在后台检测音量键消息,检测到音量键(Down 或UP)之后,启动APK设定的服务.请教大神有什么思路没? 现在按键消息只能在当前有焦点的View类获取. 解决方案 public boolean onKeyUp(int keyCode, KeyEvent event) { switch(keyCode){ case KeyEvent.KEYCODE_VOLUME_DOWN://音量下键 break; case KeyEve

Android Camera开发系列(上)——Camera的基本调用与实现拍照功能以及获取拍照图片加载大图片

Android Camera开发系列(上)--Camera的基本调用与实现拍照功能以及获取拍照图片加载大图片 最近也是在搞个破相机,兼容性那叫一个不忍直视啊,于是自己翻阅了一些基本的资料,自己实现了一个相机,虽然相机这东西,兼容性不敢恭维,但是用到的地方确实很多,所以今天,我们就一起来学习一下吧 参照Google API:http://developer.android.com/guide/topics/media/camera.html 一.Camera的启动方式 1.调用系统方式 2.自定义