本篇是Android后台杀死系列的第二篇,主要讲解ActivityMangerService是如何恢复被后台杀死的进程的(基于4.3 ),在开篇 FragmentActivity及PhoneWindow后台杀死处理机制 中,简述了后台杀死所引发的一些常见问题,还有Android系统控件对后台杀死所作的一些兼容,以及onSaveInstance跟onRestoreInstance的做用于执行时机,最后说了如何应对后台杀死,可是对于被后台杀死的进程如何恢复的并无讲解,本篇不涉及后台杀死,好比LowmemoryKiller机制,只讲述被杀死的进程如何恢复的。假设,一个应用被后台杀死,再次从最近的任务列表唤起App时候,系统是如何处理的呢?有这么几个问题可能须要解决:javascript
首先来看第一个问题,系统如何知道Application被杀死了,Android使用了Linux的oomKiller机制,只是简单的作了个变种,采用分等级的LowmemoryKiller,但这个实际上是内核层面的,LowmemoryKiller杀死进程后,不会像用户空间发送通知,也就是说框架层的ActivityMangerService没法知道App是否被杀死,可是,只有知道App或者Activity是否被杀死,AMS(ActivityMangerService)才能正确的走唤起流程,那么AMS到底是在何时知道App或者Activity被后台杀死了呢?咱们先看一下从最近的任务列表进行唤起的时候,究竟发生了什么。html
在系统源码systemUi的包里,有个RecentActivity,这个其实就是最近的任务列表的入口,而其呈现界面是经过RecentsPanelView来展示的,点击最近的App其执行代码以下:java
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就是>=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));
}...
}复制代码
在上面的代码里面,有个判断ad.taskId >= 0,若是知足这个条件,就经过moveTaskToFront唤起APP,那么ad.taskId是如何获取的?recent包里面有各种RecentTasksLoader,这个类就是用来加载最近任务列表的一个Loader,看一下它的源码,主要看一下加载:android
@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);
....
} 复制代码
能够看到,其实就是经过ActivityManger的getRecentTasks向AMS请求最近的任务信息,而后经过createTaskDescription建立TaskDescription,这里传递的recentInfo.id其实就是TaskDescription的taskId,来看一下它的意义:git
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的数目,更具体的细节能够自行查看ActivityManagerService及ActivityStack,那么这里就容易解释了,只要是存活的APP、或者被LowmemoryKiller杀死的APP,其AMS的ActivityRecord是完整保存的,这就是恢复的依据。RecentActivity获取的数据其实就是AMS中的翻版,RecentActivity并不知道将要唤起的APP是不是存活的,只要TaskRecord告诉RecentActivity是存货的,那么久直接走唤起流程,也就是经过ActivityManager的moveTaskToFront唤起App,至于后续的工做,就彻底交给AMS来处理。现看一下到这里的流程图:面试
接着往下看moveTaskToFrontLocked,这个函数在ActivityStack中,ActivityStack主要用来管理ActivityRecord栈的,全部start的Activity都在ActivityStack中保留一个ActivityRecord,这个也是AMS管理Activity的一个依据,ActivityStack最终moveTaskToFrontLocked会调用resumeTopActivityLocked来唤起Activity,AMS获取即将resume的Activity信息的方式主要是经过ActivityRecord,它并不知道Activity自己是否存活,获取以后,AMS知道唤醒Activity的环节才知道App或者Activity被杀死,具体看一下resumeTopActivityLocked源码:架构
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
// This activity is now becoming visible.
mService.mWindowManager.setAppVisibility(next.appToken, true);
.... 恢复逻辑
if (next.app != null && next.app.thread != null) {
// 正常恢复
try {
// Deliver all pending results.
ArrayList a = next.results;
if (a != null) {
final int N = a.size();
if (!next.finishing && N > 0) {
next.app.thread.scheduleSendResult(next.appToken, a);
}
}
...
next.app.thread.scheduleResumeActivity(next.appToken,
mService.isNextTransitionForward());
...
} catch (Exception e) {
// Whoops, need to restart this activity!
// 这里须要重启,难道被后台杀死,走的是异常分支吗???? 异常杀死
if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to "
+ lastState + ": " + next);
next.state = lastState;
mResumedActivity = lastResumedActivity;
<!--确实这里是由于进程挂掉了--> Slog.i(TAG, "Restarting because process died: " + next); 。。。 startSpecificActivityLocked(next, true, false); return true; } ... }复制代码
因为没有主动调用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
还有一种可能,APP没有被kill,可是Activity被Kill掉了,这个时候会怎么样?首先,Activity的管理是必定经过AMS的,Activity的kill必定是是AMS操刀的,是有记录的,严格来讲,这种状况并不属于后台杀死,由于这属于AMS正常的管理,在可控范围,好比打开了开发者模式中的“不保留活动”,这个时候,虽然会杀死Activity,可是仍然保留了ActivitRecord,因此再唤醒,或者回退的的时候仍然有迹可循,看一下ActivityStack的Destroy回调代码,框架
final boolean destroyActivityLocked(ActivityRecord r,
boolean removeFromApp, boolean oomAdj, String reason) {
...
if (hadApp) {
...
boolean skipDestroy = false;
try {
关键代码 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 {
关键代码 2
r.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是为了让避免后面的清空逻辑。ide
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,其状态是不会被设置成ActivityState.DESTROYING,是直接跳过了ActivityState.DESTROYING,被设置成了ActivityState.DESTROYED,因此不会removeActivityFromHistoryLocked,也就是保留了ActivityRecord现场,好像也是依靠异常来区分是不是正常的结束掉Activity。这种状况下是如何启动Activity的呢? 经过上面两点分析,就知道了两个关键点
这样就保证了在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现场的保存流程相对是比较简单的,入口基本就是startActivity的时候,只要是界面的跳转基本都牵扯到Activity的切换跟当前Activity场景的保存:先画个简单的图形,开偏里面讲FragmentActivity的时候,简单说了一些onSaveInstance的执行时机,这里详细看一下AMS是如何管理这些跳转以及场景保存的,模拟场景:Activity A 启动Activity B的时候,这个时候A不可见,可能会被销毁,须要保存A的现场,这个流程是什么样的:简述以下
流程大概是以下样子:
如今咱们经过源码一步一步跟一下,看看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是界面切换的统一入口,第一次进来的时候,因为ActivityA还在没有pause,所以须要先暂停ActivityA,这些完成后,
ActivityStack
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
...
<!--必须将当前Resume的Activity设置为pause 而后stop才能继续-->
// 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发送过来的pause消息以后,就会唤起ActivityB,入口仍是resumeTopActivityLocked,唤醒B,以后还会A给进一步stop掉,这个时候就牵扯到现场的保存,
ActivityStack
private final void completePauseLocked() {
if (!mService.isSleeping()) {
resumeTopActivityLocked(prev);
} else {
...
} 复制代码
ActivityB如何启动的,本文不关心,只看ActivityA如何保存现场的,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中的调用:首先经过callActivityOnSaveInstanceState,将现场保存到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动做完成,在通知的时候,还会将保存的现场数据带过去。
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的现场进行了保存,及时因为种种缘由上一个Actiivity被杀死,在回退,或者从新唤醒的过程当中AMS也能知道如何唤起Activiyt,并恢复。
如今解决两个问题,一、如何保存现场,二、AMS怎么判断知道APP或者Activity是否被异常杀死,那么就剩下最后一个问题了,AMS如何恢复被异常杀死的APP或者Activity呢。
其实在讲解AMS怎么判断知道APP或者Activity是否被异常杀死的时候,就已经涉及了恢复的逻辑,也知道了一旦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没多大区别,只是有一点须要注意,那就是这种时候启动的Activity是有上一次的现场数据传递过得去的,由于上次在退到后台的时候,全部Activity界面的现场都是被保存了,而且传递到AMS中去的,那么此次的恢复启动就会将这些数据返回给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会回调Actiivyt的onCreate,这个函数里面其实主要针对FragmentActivity作一些Fragment恢复的工做,ActivityClientRecord中的r.state是AMS传给APP用来恢复现场的,正常启动的时候,这些都是null。再来看关键点2 ,在r.state != null非空的时候执行mInstrumentation.callActivityOnRestoreInstanceState,这个函数默认主要就是针对Window作一些恢复工做,好比ViewPager恢复以前的显示位置等,也能够用来恢复用户保存数据。
打开开发者模式”不保留活动“,就是这种场景,在上面的分析中,知道,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进程,其他的都是同样的,再也不讲解。到这里,咱们应该就了解了,
到这里ActivityManagerService恢复APP场景的逻辑就应该讲完了。再碎碎念一些问题,多是一些面试的点。
Android 后台杀死系列之一:FragmentActivity 及 PhoneWindow 后台杀死处理机制
Android后台杀死系列之二:ActivityManagerService与App现场恢复机制 Android后台杀死系列之三:后台杀死原理LowMemoryKiller(4.3-6.0)
Android后台杀死系列之四:Binder讣告原理
Android后台杀死系列之五:Android进程保活-自“裁”或者耍流氓
仅供参考,欢迎指正
Android应用程序启动过程源代码分析
Android Framework架构浅析之【近期任务】
Android Low Memory Killer介绍
Android开发之InstanceState详解
对Android近期任务列表(Recent Applications)的简单分析
Android——内存管理-lowmemorykiller 机制
Android 操做系统的内存回收机制
Android LowMemoryKiller原理分析
Android进程生命周期与ADJ
Linux下/proc目录简介
startActivity启动过程分析 精
Activity销毁流程