Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx
- 文档编号:24904093
- 上传时间:2023-06-02
- 格式:DOCX
- 页数:23
- 大小:395.12KB
Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx
《Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx》由会员分享,可在线阅读,更多相关《Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx(23页珍藏版)》请在冰豆网上搜索。
Android后台杀死系列之二ActivityManagerService与App现场恢复机制
Android后台杀死系列之二:
ActivityManagerService与App现场恢复机制
本篇是Android后台杀死系列的第二篇,主要讲解ActivityMangerService是如何恢复被后台杀死的进程的(基于4.3),在开篇FragmentActivity及PhoneWindow后台杀死处理机制中,简述了后台杀死所引起的一些常见问题,还有Android系统控件对后台杀死所做的一些兼容,以及onSaveInstance跟onRestoreInstance的作用于执行时机,最后说了如何应对后台杀死,但是对于被后台杀死的进程如何恢复的并没有讲解,本篇不涉及后台杀死,比如LowmemoryKiller机制,只讲述被杀死的进程如何恢复的。
作者:
佚名来源:
segmentfault|2017-01-1215:
06
收藏
分享
本篇是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
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
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
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.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 后台 杀死 系列 ActivityManagerService App 现场 恢复 机制
链接地址:https://www.bdocx.com/doc/24904093.html