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

上传人:b****4 文档编号:24904093 上传时间:2023-06-02 格式:DOCX 页数:23 大小:395.12KB
下载 相关 举报
Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx_第1页
第1页 / 共23页
Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx_第2页
第2页 / 共23页
Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx_第3页
第3页 / 共23页
Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx_第4页
第4页 / 共23页
Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

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

《Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx》由会员分享,可在线阅读,更多相关《Android后台杀死系列之二ActivityManagerService与App现场恢复机制.docx(23页珍藏版)》请在冰豆网上搜索。

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

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 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 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 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.

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 农学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1