Android窗口管理服务WindowManagerService切换Activity窗口(App Transition

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

        在Android系统中,同一时刻只有一个Activity组件是处于激活状态的,因此,当ActivityManagerService服务激活了一个新的Activity组件时,它就需要通知WindowManagerService服务将该Activity组件的窗口显示出来,这会涉及到将焦点和屏幕等资源从前一个激活的Activity组件切换到后一个激活的Activity组件的过程,本文就详细分析这个过程。

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

         Activity窗口的切换操作是在新激活的Activity组件的启动过程进行的。具体来说,就是在前一个激活的Activity组件进入到Paused状态并且新激活的Activity组件进之到Resumed 状态之后,将前一个激活的Activity组件的窗口设置为不可见,以及将新激活的Activity组件的窗口设置为可见。整个切换过程是需要在ActivityManagerService服务和WindowManagerService服务的协作之下进行的,如图1所示。


图1 Activity窗口的切换操作示意图

        WindowManagerService服务在执行Activity窗口的切换操作的时候,会给参与切换操作的Activity组件的设置一个动画,以便可以向用户展现一个Activity组件切换效果,从而提高用户体验。事实上,一个Activity窗口在由不可见状态切换到可见状态的过程中,除了会被设置一个Activity组件切换动画之外,还有被设置一个窗口进入动画,此外,如果该Activity窗口是附加在另外一个窗口之上的,并且这个被附加的窗口正在显示一个动画,那么这个动画也同时会被设置给到该Activity窗口的显示过程中去。本文主要是关注Activity窗口的切换操作,在接下来的一篇文章中分析窗口的动画框架时,我们再详细分析上述三种动画是如何作用在窗口的显示过程中的。

        从前面Android应用程序启动过程源代码分析一文可以知道,ActivityManagerService服务在启动一个Activity组件的过程中,会调用到ActivityStack类的成员函数startActivityLocked。ActivityStack类的成员函数startActivityLocked首先会给正在启动的Activity组件准备一个切换操作,接着再调用其它的成员函数来通知前一个激活的Activity组件进入到Paused状态。等到前一个激活的Activity组件进入到Paused状态之后,ActivityManagerService服务就会检查用来运行正在启动的Activity组件的进程启动起来了没有。如果这个进程还没有启动,那么ActivityManagerService服务就会将该进程启动起来,然后再调用ActivityStack类的成员函数realStartActivityLocked来将正在启动的Activity组件加载起来,并且将它的状态设置为Resumed,最后通知WindowManagerService服务执行前面所准备的切换操作。

        接下来,我们就从ActivityStack类的成员函数startActivityLocked开始分析Activity窗口的切换过程,如图2所示。


图2 Activity窗口的切换过程

        这个过程可以分为9个步骤,接下来我们就详细分析每一个步骤。

        Step 1. ActivityStack.startActivityLocked

public class ActivityStack {    ......    private final void startActivityLocked(ActivityRecord r, boolean newTask,            boolean doResume) {        final int NH = mHistory.size();        int addPos = -1;        if (!newTask) {            // If starting in an existing task, find where that is...            ......        }        // Place a new activity at top of stack, so it is next to interact        // with the user.        if (addPos < 0) {            addPos = NH;        }        ......        // Slot the activity into the history stack and proceed        mHistory.add(addPos, r);        ......        if (NH > 0) {            ......            if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {                mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);                ......            } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {                mService.mWindowManager.prepareAppTransition(                        WindowManagerPolicy.TRANSIT_TASK_OPEN);                ......            } else {                mService.mWindowManager.prepareAppTransition(newTask                        ? WindowManagerPolicy.TRANSIT_TASK_OPEN                        : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);                ......            }            ......        }        ......        if (doResume) {            resumeTopActivityLocked(null);        }    }    ......}

        这个函数定义在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。

        参数r描述的是正在启动的Activity组件,ActivityStack类的成员函数startActivityLocked首先找到它在Activity组件堆栈中的位置addPos,即找到它在ActivityStack类的成员变量mHistory所描述的一个ArrayList中的位置,然后再将正在启动的Activity组件保存在该位置中。

        变量NH描述的是将参数r描述的是正在启动的Activity组件保存在Activity组件堆栈前系统已经启动了的Activity组件的个数。只有在这个变量的值大于0的情况下,系统才需要执行一个Activity组件切换操作。也就是说,如果参数r描述的是正在启动的Activity组件是系统中第一个启动的Activity组件,那么就不需要执行一个Activity组件切换操作了。

        注意,即使参数r描述的是正在启动的Activity组件不是系统中第一个启动的Activity组件,那么系统也可能不需要执行一个Activity组件切换操作,因为用来启动参数r所描述的一个Activity组件的一个Intent对象的成员函数getFlags返回的一个标志值的Intent.FLAG_ACTIVITY_NO_ANIMATION位可能会不等于0,这意味着正在启动的Activity组件不需要显示切换动画。在这种情况下,ActivityManagerSerivce服务就会通知WindowManagerService服务不需要准备一个Activity组件切换操作,这是通过以WindowManagerPolicy.TRANSIT_NONE为参数来调用ActivityStack类的成员变量mService所指向的一个ActivityManagerService对象的成员变量mWindowManager所描述的一个WindowManagerService对象的成员函数prepareAppTransition来实现的。

        另一方面,如果参数r描述的是正在启动的Activity组件不是系统中第一个启动的Activity组件,并且系统需要执行一个Activity组件切换操作,即需要WindowManagerService服务在显示正在启动的Activity组件的窗口时应用一个切换动画,那么这个动画的类型也是有讲究的。具体来说,如果参数r描述的是Activity组件是需要在在一个新的任务中启动的,即参数newTask的值等于true,那么切换动画的类型就指定为WindowManagerPolicy.TRANSIT_TASK_OPEN,否则的话,切换动画的类型就指定为WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN。

        此外,如果用来启动参数r所描述的一个Activity组件的一个Intent对象的成员函数getFlags返回的一个标志值的Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET位不等于0,那么也会将切换动画的类型设置为WindowManagerPolicy.TRANSIT_TASK_OPEN。Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET这个标志位等于1意味着当参数r所描述的一个Activity组件所在的任务下次作为前台任务来运行时,参数r所描述的Activity组件以及它上面的并且属于同一个任务的其它Activity组件都会被结束掉,以使得位于参数r所描述的Activity组件的前面一个Activity组件可以显示出来。例如,如果我们正在使用一个Email Activity组件来查看Email,这时候又需要启动另外一个Pictrue Activity来查看该Email附件的一张图片。这时候Email Activity和Pictrue Activity就是在同一个任务中的。突然间,我们因为其它原历,需要按下Home键回到Launcher中去完成其它任务。完成其它任务之后,再点击Launcher上面的Email Activity图标来想重新浏览之前正在查看的Email,但是又不想看到的是该Email附件的那张图片。在这种情况下,之前在启动Pictrue Activity来查看Email的附件图片时,就可以将用来启动Pictrue Activity的一个Intent对象的标志值的Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET位设置为1。

        无论如何,最终指定的切换动画的类型都是通过调用ActivityStack类的成员变量mService所指向的一个ActivityManagerService对象的成员变量mWindowManager所描述的一个WindowManagerService对象的成员函数prepareAppTransition来通知WindowManagerService服务的。

        ActivityStack类的成员函数startActivityLocked通知WindowManagerService服务准备好一个Activity组件切换操作之后,如果参数doResume的值等于true,那么它就会继续调用另外一个成员函数resumeTopActivityLocked来继续执行启动参数r所描述的一个Activity组件的操作。

        接下来,我们就首先分析WindowManagerService类的成员函数prepareAppTransition的实现,以便可以了解WindowManagerService服务是如何准备一个Activity组件切换操作的,然后再回过头来分析ActivityStack类的成员函数resumeTopActivityLocked是如何继续执行启动参数r所描述的一个Activity组件的操作的。

        Step 2. WindowManagerService.prepareAppTransition

public class WindowManagerService extends IWindowManager.Stub        implements Watchdog.Monitor {    ......    // State management of app transitions.  When we are preparing for a    // transition, mNextAppTransition will be the kind of transition to    // perform or TRANSIT_NONE if we are not waiting.  If we are waiting,    // mOpeningApps and mClosingApps are the lists of tokens that will be    // made visible or hidden at the next transition.    int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;    ......    boolean mAppTransitionReady = false;    ......    boolean mAppTransitionTimeout = false;    boolean mStartingIconInTransition = false;    boolean mSkipAppTransitionAnimation = false;    public void prepareAppTransition(int transit) {        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,                "prepareAppTransition()")) {            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");        }        synchronized(mWindowMap) {            ......            if (!mDisplayFrozen && mPolicy.isScreenOn()) {                if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET                        || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {                    mNextAppTransition = transit;                } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN                        && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) {                    // Opening a new task always supersedes a close for the anim.                    mNextAppTransition = transit;                } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN                        && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) {                    // Opening a new activity always supersedes a close for the anim.                    mNextAppTransition = transit;                }                mAppTransitionReady = false;                mAppTransitionTimeout = false;                mStartingIconInTransition = false;                mSkipAppTransitionAnimation = false;                mH.removeMessages(H.APP_TRANSITION_TIMEOUT);                mH.sendMessageDelayed(mH.obtainMessage(H.APP_TRANSITION_TIMEOUT),                        5000);            }        }    }    ......}
        这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        调用WindowManagerService类的成员函数prepareAppTransition来通知WindowManagerService服务准备一个Activity组件切换操作是需要具有android.Manifest.permission.MANAGE_APP_TOKENS,否则的话,WindowManagerService类的成员函数prepareAppTransition就会抛出一个类型为SecurityException的异常。

        WindowManagerService类的成员变量mNextAppTransition描述的就是WindowManagerService服务接下来要执行一个Activity组件切换操作的类型,也就是要执行的一个Activity组件切换动画的类型。WindowManagerService类的成员函数prepareAppTransition按照以下规则来设置WindowManagerService服务接下来要执行的Activity组件切换动画的类型:

        1. 当 WindowManagerService类的成员变量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_UNSET或者WindowManagerPolicy.TRANSIT_NONE的时候,就说明WindowManagerService服务接下来没有Activity组件切换动画等待执行的,这时候参数transit所描述的Activity组件切换动画就可以作为WindowManagerService服务接下来要执行的Activity组件切换动画。

        2. 当WindowManagerService类的成员变量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_TASK_CLOSE,那么就说明WindowManagerService服务接下来要执行一个关闭Activity组件任务的切换动画等待执行的。在这种情况下,如果参数transit所描述的是一个打开Activity组件任务的切换动画,即它的值等于WindowManagerPolicy.TRANSIT_TASK_OPEN,那么就需要将WindowManagerService服务接下来要执行的Activity组件切换动画为打开Activity组件任务类型的。这是因为打开Activity组件任务的切换动画的优先级高于关闭Activity组件任务的切换动画。

        3. 当WindowManagerService类的成员变量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE,那么就说明WindowManagerService服务接下来要执行一个关闭Activity组件的切换动画等待执行的。在这种情况下,如果参数transit所描述的是一个打开Activity组件的切换动画,即它的值等于WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN,那么就需要将WindowManagerService服务接下来要执行的Activity组件切换动画为打开Activity组件类型的。这是因为打开Activity组件的切换动画的优先级高于关闭Activity组件的切换动画。

        设置好WindowManagerService服务接下来要执行的Activity组件切换动画的类型之后,WindowManagerService类的成员函数prepareAppTransition还会将其余四个成员变量mAppTransitionReady、mAppTransitionTimeout、mStartingIconInTransition和mSkipAppTransitionAnimation的值设置为false,其中:

        1. mAppTransitionReady表示WindowManagerService服务可以开始执行一个Activity组件的切换动画了没有?

        2. mAppTransitionTimeout表示WindowManagerService服务正在执行的Activity组件切换动画是否已经超时?

        3. mStartingIconInTransition表示WindowManagerService服务开始显示正在启动的Activity组件的启动窗口了没有?

        4. mSkipAppTransitionAnimation表示WindowManagerService服务是否需要不执行Activity组件的切换动画?

        最后,WindowManagerService类的成员函数prepareAppTransition还会调用成员变量mH所描述的一个H对象的成员函数sendMessageDelayed来向WindowManagerService服务所运行在的线程发送一个类型为APP_TRANSITION_TIMEOUT的消息。这个消息将在5秒后被执行,是用来强制前面所设置的Activity组件切换动画要在5秒之内执行完成的,否则的话,WindowManagerService服务就会认为该切换动画执行超时了。

        这一步执行完成之后,WindowManagerService服务接下来要执行的Activity组件切换操作或者说切换动画就准备完成了。注意,这时候只是准备好Activity组件切换动画,但是这个切换动画还不能执行,要等到前一个激活的Activity组件进入到Paused状态并且接下来正在启动的Activity组件进入到Resumed状态之后才能执行。

        回到前面的Step 1中,即ActivityStack类的成员函数startActivityLocked,接下来它就会调用另外一个成员函数resumeTopActivityLocked来继续启动指定的Activity组件。从前面Android应用程序启动过程源代码分析一文可以知道,ActivityStack类的成员函数resumeTopActivityLocked以及接下来要调用的其它成员函数就是执行以下两个操作:

        1. 通知当前处于激活状态的Activity组件所运行在的应用程序进程,它所运行的一个Activity组件要由Resumed状态进入到Paused状态了。

        2. 检查用来运行当前正在启动的Activity组件的应用程序进程是否已经启动起来了。如果已经启动起来,那么就会直接通知该应用程序进程将正在启动的Activity组件加载起来,否则的话,就会先将该应用程序进程启动起来,然后再通知它将正在启动的Activity组件加载起来。

        在第2步中,通知相应的应用程序进程将正在启动的Activity组件加载起来是通过调用ActivityStack类的成员函数realStartActivityLocked来实现的,接下来我们就继续分析这个成员函数的实现,以便可以继续了解Activity组件的切换操作的执行过程。

        Step 3. ActivityStack.realStartActivityLocked

public class ActivityStack {    ......    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值