activity 变成后台进程后被杀死_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其执行代码如下:

publicvoid handleOnClick(Viewview) {

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 isan active task; it should just gotothe 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,看一下它的源码,主要看一下加载:

@Override

protected Void doInBackground(Void... params) {

// We loadintwo stages:first, weupdateprogresswithjust thefirstscreenful

// ofitems.Then, weupdatewiththe restofthe items

final intorigPri = 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 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,来看一下它的意义:

publicList getRecentTasks(intmaxNum,

intflags,intuserId) {

...

IPackageManager pm = AppGlobals.getPackageManager();

final intN = mRecentTasks.size();

...

for(inti=0; i 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来处理。现看一下到这里的流程图:

在唤起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源码:

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {

// This activity isnow becoming visible.

mService.mWindowManager.setAppVisibility(next.appToken,true);

....    恢复逻辑

if (next.app !=null&&next.app.thread !=null) {

// 正常恢复

try {

// Deliver allpending results.

ArrayList a = next.results;

if (a != null) {

final intN = 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 torestart 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);

returntrue;

}

...

}

由于没有主动调用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回调代码,

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;

}

}

returnremovedFromHistory;

}

这里有两个关键啊你单,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);

}

intindex= indexOfActivityLocked(r);

if (index>= 0) {

1  

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的呢? 通过上面两点分析,就知道了两个关键点

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 torestart this activity!

...

startSpecificActivityLocked(next,true,true);

}

returntrue;

}

到这里,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

publicfinalintstartActivityAsUser(IApplicationThread caller, String callingPackage,

Intent intent, String resolvedType, IBinder resultTo,

String resultWho, intrequestCode,intstartFlags,

String profileFile, ParcelFileDescriptor profileFd, Bundle options, intuserId) {

enforceNotIsolatedCaller("startActivity");

...

returnmMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,

resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,

null,null, options, userId);

}

ActivityStack

finalintstartActivityMayWait(IApplicationThread caller,intcallingUid,

intres = 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) {

...

// We need tostart pausing thecurrentactivity so thetopone

// can be resumed...

if (mResumedActivity != null) {

if (next.app !=null&&next.app.thread !=null) {

mService.updateLruProcessLocked(next.app,false);

}

startPausingLocked(userLeaving, false);

returntrue;

}

....

其实这里就是暂停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, intconfigChanges) {

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) {

...

// Nexthave the activity save itscurrentstateandmanaged 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动作完成,在通知的时候,还会将保存的现场数据带过去。

privatestaticclass StopInfo implements Runnable {

ActivityClientRecord activity;

Bundle state;

Bitmap thumbnail;

CharSequence description;

@Override publicvoid 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,并恢复。

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

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

其实在讲解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 torestart 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);

returntrue;

}

从上面的代码可以知道,其实就是走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恢复之前的显示位置等,也可以用来恢复用户保存数据。

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

打开开发者模式”不保留活动“,就是这种场景,在上面的分析中,知道,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 torestart this activity!

startSpecificActivityLocked(next,true,true);

}

returntrue;

}

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

Android是如何在预防的情况下保存场景

AMS如何知道APP是否被后台杀死

AMS如何根据ActivityStack重建APP被杀死时的场景

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

主动清除最近任务跟异常杀死的区别:ActivityStack是否正常清楚

恢复的时候,为什么是倒序恢复:因为这是ActivityStack中的HistoryRecord中栈的顺序,严格按照AMS端来

一句话概括Android后台杀死恢复原理:Application进程被Kill,但现场被AMS保存,AMS能根据保存恢复Application现场

【编辑推荐】

【责任编辑:枯木 TEL:(010)68476606】

点赞 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值