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

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

android 切到前台 安卓切换窗口_WMS

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

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

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

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

android 切到前台 安卓切换窗口_切换动画_02

图2 Activity窗口的切换过程


ActivityStack的startActivityLocked函数

我们先看看ActivityStack的startActivityLocked函数

final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {
        ......

        // Slot the activity into the history stack and proceed
        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
                new RuntimeException("here").fillInStackTrace());
        task.addActivityToTop(r);
        task.setFrontOfTask();

        r.putInHistory();
        if (!isHomeStack() || numActivities() > 0) {
            ......
            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);
                mNoAnimActivities.add(r);
            } else {
                mWindowManager.prepareAppTransition(newTask
                        ? r.mLaunchTaskBehind
                                ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
                                : AppTransition.TRANSIT_TASK_OPEN
                        : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                mNoAnimActivities.remove(r);
            }
            mWindowManager.addAppToken(task.mActivities.indexOf(r),
                    r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                    (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
                    r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
            ......

        if (doResume) {
            mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
        }
    }

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

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

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

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

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


WMS的prepareAppTransition函数

下面我们就来分析下WMS的prepareAppTransition函数:

public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
        ......

        synchronized(mWindowMap) {
            if (!mAppTransition.isTransitionSet() || mAppTransition.isTransitionNone()) {
                mAppTransition.setAppTransition(transit);
            } else if (!alwaysKeepCurrent) {
                if (transit == AppTransition.TRANSIT_TASK_OPEN
                        && mAppTransition.isTransitionEqual(
                                AppTransition.TRANSIT_TASK_CLOSE)) {
                    // Opening a new task always supersedes a close for the anim.
                    mAppTransition.setAppTransition(transit);
                } else if (transit == AppTransition.TRANSIT_ACTIVITY_OPEN
                        && mAppTransition.isTransitionEqual(
                            AppTransition.TRANSIT_ACTIVITY_CLOSE)) {
                    // Opening a new activity always supersedes a close for the anim.
                    mAppTransition.setAppTransition(transit);
                }
            }
            if (okToDisplay() && mAppTransition.prepare()) {
                mSkipAppTransitionAnimation = false;
            }
            if (mAppTransition.isTransitionSet()) {
                mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
                mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, 5000);
            }
        }
    }

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

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

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

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

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

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

case APP_TRANSITION_TIMEOUT: {
                    synchronized (mWindowMap) {
                        if (mAppTransition.isTransitionSet() || !mOpeningApps.isEmpty()
                                    || !mClosingApps.isEmpty()) {
                            mAppTransition.setTimeout();//设置timeout状态
                            performLayoutAndPlaceSurfacesLocked();//时间到了强制刷新
                        }
                    }
                    break;
                }

setTimeout只是设置了一个状态


void setTimeout() {
        mAppTransitionState = APP_STATE_TIMEOUT;
    }


而setAppTransition是设置Activity下一个切换类型


void setAppTransition(int transit) {
        mNextAppTransition = transit;
    }



 ActivityStackSupervisor的realStartActivityLocked函数

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

        回到前面的Step 1中,即ActivityStack类的成员函数startActivityLocked,接下来它就会调用另外一个成员函数resumeTopActivityLocked来继续启动指定的Activity组件。ActivityStack类的成员函数resumeTopActivityLocked以及接下来要调用的其它成员函数就是执行以下两个操作:

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

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

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

final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig)
            throws RemoteException {

        if (andResume) {
            r.startFreezingScreenLocked(app, 0);
            mWindowManager.setAppVisibility(r.appToken, true);

            // schedule launch ticks to collect information about slow apps.
            r.startLaunchTickingLocked();
        }

        try {
            ......
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);

            ......
        } catch (RemoteException e) {
            .....
        }

        ......
        if (andResume) {
            stack.minimalResumeActivityLocked(r);
        } else {
            r.state = STOPPED;
            r.stopped = true;
        }
        ......

        return true;
    }

上面这个函数主要有如下步骤:

1. 通知WindowManagerService服务将参数r所描述的Activity组件的可见性设置为true,这是通过调用ActivityStack类的成员变量mService所指向的一个ActivityManagerService对象的成员变量mWindowManager所描述的一个WindowManagerService对象的成员函数setAppVisibility来实现的。

2. 通知参数r所描述的Activity组件所运行在的应用程序进程将它加载起来,这是通过调用参数app所指向的一个ProcessRecord对象的成员变量thread所描述的一个类型为ApplictionThread的Binder代理对象的成员函数scheduleLaunchActivity来实现的。

3. 当参数andResume的值等于true的时候,就表示在执行第2步时,参数r所描述的Activity组件所运行在的应用程序进程已经将它的状态设置为Resumed了,即已经调用过它的成员函数onResume了。在这种情况,ActivityManagerService服务也需要将该Activity组件的状态设置为Resumed了,即将r所指向的一个ActivityRecord对象的成员变量state的值设置为ActivityState.RESUMED,并且将ActivityStack类的成员变量mResumedActivity的值设置为r,以便表示当前激活的Activity组件为参数r所描述的Activity组件。最后,ActivityManagerService服务还需要调用ActivityStack类的成员函数minimalResumeActivityLocked函数。而minimalResumeActivityLocked主要调用了completeResumeLocked函数。completeResumeLocked函数来通知WindowManagerService服务执行在前面Step 2所准备好的Activity组件切换操作。


        接下来,我们首先分析WindowManagerService类的成员函数setAppVisibility的实现,以便可以了解WindowManagerService服务是如何设置一个Activity组件的可见性的,接着再分析ActivityStack类的成员函数completeResumeLocked的实现,以便可以了解ActivityManagerService服务是如何通知WindowManagerService服务执行前面所准备好的一个Activity组件切换操作的。


WMS的setAppVisibility函数

public void setAppVisibility(IBinder token, boolean visible) {
        ......
        AppWindowToken wtoken;

        synchronized(mWindowMap) {
            wtoken = findAppWindowToken(token);//找到APPWindowToken
            if (wtoken == null) {
                Slog.w(TAG, "Attempted to set visibility of non-existing app token: " + token);
                return;
            }

            mOpeningApps.remove(wtoken);
            mClosingApps.remove(wtoken);
            wtoken.waitingToShow = false;
            wtoken.hiddenRequested = !visible;

            // If we are preparing an app transition, then delay changing
            // the visibility of this token until we execute that transition.
            if (okToDisplay() && mAppTransition.isTransitionSet()) {
                if (!wtoken.mAppAnimator.usingTransferredAnimation &&
                        (!wtoken.startingDisplayed || mSkipAppTransitionAnimation)) {
                    wtoken.mAppAnimator.setDummyAnimation();
                }
                wtoken.inPendingTransaction = true;
                if (visible) {
                    mOpeningApps.add(wtoken);//把要打开的应用的APPWindowToken放入mOpeningApps中
                    wtoken.startingMoved = false;
                    wtoken.mEnteringAnimation = true;

                    if (wtoken.hidden) {
                        wtoken.allDrawn = false;
                        wtoken.deferClearAllDrawn = false;
                        wtoken.waitingToShow = true;

                        if (wtoken.clientHidden) {
                            wtoken.clientHidden = false;
                            wtoken.sendAppVisibilityToClients();
                        }
                    }
                } else {
                    mClosingApps.add(wtoken);
                    wtoken.mEnteringAnimation = false;
                }
                if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
                    // We're launchingBehind, add the launching activity to mOpeningApps.
                    final WindowState win =
                            findFocusedWindowLocked(getDefaultDisplayContentLocked());
                    if (win != null) {
                        final AppWindowToken focusedToken = win.mAppToken;
                        if (focusedToken != null) {
                            focusedToken.hidden = true;
                            mOpeningApps.add(focusedToken);
                        }
                    }
                }
                return;
            }
	    .....
            setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET,//设置
                    true, wtoken.voiceInteraction);
            wtoken.updateReportedVisibilityLocked();
            .....
        }
    }

        每一个Activity组件在WindowManagerService服务内部都对应有一个AppWindowToken对象,用来描述该Activity组件的窗口在WindowManagerService服务中的状态。因此,WindowManagerService类的成员函数setAppVisibility就先通过调用成员函数findAppWindowToken来找到与参数token所描述的一个Activity组件所对应的一个AppWindowToken对象wtoken,以便接下来可以设置它的状态。

即WindowManagerService类的成员变量mNextAppTransition的值是否等于WindowManagerPolicy.TRANSIT_UNSET。如果是TRANSIT_UNSET的话,只要简单地执行以下两个操作即可。

        1. 调用WindowManagerService类的成员函数setTokenVisibilityLocked来将参数token所描述的Activity组件的可见性设置为参数visible所描述的值;(这个函数还会设置动画,当有改变的时候还会调用performLayoutAndPlaceSurfacesLocked刷新系统)

        2. 调用AppWindowToken对象wtoken的成员函数updateReportedVisibilityLocked来向ActivityManagerService服务报告参数token所描述的Activity组件的可见性。

如果不是TRANSIT_UNSET,也是就是已经设置了Transit,那么参数token所描述的Activity组件就是正在等待执行切换操作的Activity组件,这时候修改的可见性就会复杂一些:

        我们假设WindowManagerService服务接下来需要执行一个Activity组件操作,即WindowManagerService类的成员变量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_UNSET,这时候还需要满足okToDisplay函数即:

        1. 屏幕当前不是处于冻结状态,即WindowManagerService类的成员变量mDisplayFrozen的值等于false;

        2. 屏幕当前是点亮的,即WindowManagerService类的成员变量mPolicy所指向的一个PhoneWindowManager对象的成员函数isScreenOn的返回值等于true;

         那么WindowManagerService类的成员函数setAppVisibility接下来才会开始修改参数token所描述的一个Activity组件的可见性,即:

        1. 将AppWindowToken对象wtoken的成员变量hiddenRequested的值设置为参数visible的相反值。也就是说,如果参数token所描述的Activity组件是可见的,那么就将AppWindowToken对象wtoken的成员变量hiddenRequested的值设置为false,否则的话,就设置为true。

        2. 调用AppWindowToken对象wtoken的成员函数setDummyAnimation来给参数token所描述的Activity组件设置一个哑动画。注意,要等到执行参数token所描述的Activity组件的切换操作时,WindowManagerService服务才会给该Activity组件设置一个合适的切换动画。

        3. 分别将AppWindowToken对象wtoken从WindowManagerService类的成员变量mOpeningApps和mClosingApps所描述的两个ArrayList中删除。注意,WindowManagerService类的成员变量mOpeningApps和mClosingApps保存的分别是系统当前正在打开和关闭的Activity组件,后面会根据参数visible的值来决定参数token所描述的Activity组件是正在打开的还是正在关闭的,以便可以将它放在WindowManagerService类的成员变量mOpeningApps或者mClosingApps中。

        4. 将AppWindowToken对象wtoken的成员变量waitingToShow和waitingToHide的值都初始化为false,表示参数token所描述的Activity组件既不是正在等待显示的,也不是正在等待隐藏的,这两个成员变量的值同样是要根据参数visible的值来设置的。

        5. 将AppWindowToken对象wtoken的成员变量inPendingTransaction的值设置为true,表示参数token所描述的Activity组件正在等待切换。

        接下来的操作取决于参数visible的值是true还是false。

        假设参数visible的值等于true,那么就表示要将参数token所描述的Activity组件设置可见,这时候WindowManagerService类的成员函数setAppVisibility会继续执行以下操作:

        1. 将AppWindowToken对象wtoken添加WindowManagerService类的成员变量mOpeningApps所描述的一个ArrayList中,表示参数token所描述的Activity组件是正在打开的。

        2. 将AppWindowToken对象wtoken的成员变量startingDisplayed和startingMoved的值都设置为false,表示参数token所描述的Activity组件的启动窗口还没有显示出来,以及也没有被转移给其它的Activity组件。

        3. 如果AppWindowToken对象wtoken的成员变量hidden的值等于true,那么就意味着参数token所描述的Activity组件当前是不可见的。由于在这种情况下,参数token所描述的Activity组件正在等待打开,因此,该Activity组件的窗口一定是还没有绘制出来,并且正在等待绘制以及显示出来,这时候就需要将AppWindowToken对象wtoken的成员变量allDrawn和waitingToShow的值分别设置为false和true。

        4.  如果AppWindowToken对象wtoken的成员变量hidden的值等于true,并且另外一个成员变量clientHidden的值也等于true,那么就说明在应用程序进程这一侧看来,参数token所描述的Activity组件是不可见的,这时候就需要让该应用程序进程认为参数token所描述的Activity组件是可见的,以便该应用程序进程可以将参数token所描述的Activity组件的窗口绘制出来,这样WindowManagerService服务接下来才可以将该Activity组件的窗口显示出来。通知应用程序进程将参数token所描述的Activity组件设置为true是通过调用AppWindowToken对象wtoken的成员函数sendAppVisibilityToClients来实现的,同时在通知之前,也会将AppWindowToken对象wtoken的成员变量clientHidden设置为false。

        假设参数visible的值等于false,那么就表示要将参数token所描述的Activity组件设置不可见,这时候WindowManagerService类的成员函数setAppVisibility会继续执行以下操作:

        1. 将AppWindowToken对象wtoken添加WindowManagerService类的成员变量mClosingApps所描述的一个ArrayList中,表示参数token所描述的Activity组件是正在关闭的。

        2. 如果AppWindowToken对象wtoken的成员变量hidden的值等于false,那么就意味着参数token所描述的Activity组件当前是可见的。由于在这种情况下,参数token所描述的Activity组件正在等待关闭,因此,该Activity组件的窗口接下来的状态应该等待隐藏不见的,这时候就需要将AppWindowToken对象wtoken的成员变量waitingToHide的值设置为true。

        这一步执行完成之后,参数token所描述的Activity组件的可见性就设置好了,回到前面的Step 3中,即ActivityStack类的成员函数realStartActivityLocked中,接下来就会通知参数token所描述的Activity组件所运行在的应用程序进程将它加载起来,并且最后调用ActivityStack类的成员函数completeResumeLocked来通知WindowManagerService服务执行在前面Step 2所准备好的Activity组件切换操作。


 ActivityStack的completeResumeLocked函数

private void completeResumeLocked(ActivityRecord next) {
        ......
        mStackSupervisor.scheduleIdleTimeoutLocked(next);

        mStackSupervisor.reportResumedActivityLocked(next);

        ......
    }

  ActivityStack类的成员函数completeResumeLocked主要是执行以下三个操作:

        1. 调用ActivityStackSupervisor的scheduleIdleTimeoutLocked函数向AMS所运行在的线程发送一个类型为IDLE_TIMEOUT_MSG的消息,这个消息将在IDLE_TIMEOUT毫秒后处理。这个类型为IDLE_TIMEOUT_MSG实际是用来监控WindowManagerService服务能否在IDLE_TIMEOUT毫秒之内,完成参数next所描述的Activity组件的切换操作,并且将它的窗口显示出来。如果能够到的话,WindowManagerService服务就会通知ActivityManagerService服务,然后ActivityManagerService服务就会执行一些清理工作,例如,将那些已经处于Stopped状态的Activity组件清理掉。如果不能够做到的话,那么ActivityStack类的成员函数completeResumeLocked也需要保证在IDLE_TIMEOUT毫秒之后,ActivityManagerService服务能够执行上述的清理工作。

        2. 调用ActivityStackSupervisor的eportResumedActivityLocked函数。

我们来看下ActivityStackSupervisor的eportResumedActivityLocked函数。

boolean reportResumedActivityLocked(ActivityRecord r) {
        final ActivityStack stack = r.task.stack;
        if (isFrontStack(stack)) {
            mService.updateUsageStats(r, true);
        }
        if (allResumedActivitiesComplete()) {
            ensureActivitiesVisibleLocked(null, 0);
            mWindowManager.executeAppTransition();
            return true;
        }
        return false;
    }

这个函数当Activity完成onResume之后,会调用ensureActivitiesVisibleLocked函数(检查哪些Activity组件是需要设置为可见的,哪些Activity组件是需要设置为不可见的)和WMS的executeAppTransition函数(通知WindowManagerService服务执行在前面Step 2所准备好的Activity组件切换操作)。

这里我们再看下WMS的executeAppTransition函数


WMS的executeAppTransition函数

public void executeAppTransition() {
        ......

        synchronized(mWindowMap) {
            if (mAppTransition.isTransitionSet()) {
                mAppTransition.setReady();
                final long origId = Binder.clearCallingIdentity();
                try {
                    performLayoutAndPlaceSurfacesLocked();
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }
        }
    }

executeAppTransition函数逻辑很简单,先看mAppTransition(下一个切换操作)是否被设置过,然后调用setReady设置状态为准备状态。接着就调用performLayoutAndPlaceSurfacesLocked刷新系统。


WMS的handleAppTransitionReadyLocked函数

在WMS的performLayoutAndPlaceSurfacesLocked关于Activity切换的代码主要如下:

if (mAppTransition.isReady()) {
            defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
                    defaultDisplay.pendingLayoutChanges);
        }

这里mAPPTransition的isReady函数就是在executeAppTransition函数中将下一个Activity的操作状态设置成准备状态的。 我们主要看下handleAppTransitionReadyLocked函数,这个函数比较长我们分段分析:

public int handleAppTransitionReadyLocked(WindowList windows) {
        int changes = 0;
        int i;
        int appsCount = mOpeningApps.size();
        boolean goodToGo = true;
        if (!mAppTransition.isTimeout()) {
            for (i = 0; i < appsCount && goodToGo; i++) {
                AppWindowToken wtoken = mOpeningApps.valueAt(i);
                if (!wtoken.allDrawn && !wtoken.startingDisplayed
                        && !wtoken.startingMoved) {
                    goodToGo = false;
                }
            }

系统当前所有正在打开的Activity组件都保存在WindowManagerService类的成员变量mOpeningApps所描述的一个ArrayList中,因此,这段代码就通过遍历这个ArrayList来检查每一个正在打开的Activity组件的UI是否已经绘制完成,即检查对应的AppWindowToken对象的成员变量allDraw的值是否不等于true。如果不等于true的话,就说明还没有绘制完成。此外,还要求这些正在打开的Activity组件的启动窗口已经显示结束,或者已经转移给其它的Activity组件,即要求查对应的AppWindowToken对象的成员变量startingDisplayed和startingMoved的值均等于true。只要其中的一个正在打开的Activity组件不能满足上述条件,那么变量goodToGo的值就会等于false,表示这时候还不能执行Activity组件操作。


下面很大篇幅是壁纸相关的我们就不分析了。

我们继续分析剩下的,主要就是mOpeningApps和mClosingApps中要打开的app和关闭的app的动画设置。

appsCount = mOpeningApps.size();//要启动的app的APPWindowToken
            for (i = 0; i < appsCount; i++) {
                AppWindowToken wtoken = mOpeningApps.valueAt(i);
                final AppWindowAnimator appAnimator = wtoken.mAppAnimator;

                if (!appAnimator.usingTransferredAnimation) {
                    appAnimator.clearThumbnail();
                    appAnimator.animation = null;
                }
                wtoken.inPendingTransaction = false;
                if (!setTokenVisibilityLocked(
                        wtoken, animLp, true, transit, false, voiceInteraction)){//设置动画
                    mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
                }
                wtoken.updateReportedVisibilityLocked();
                wtoken.waitingToShow = false;

                appAnimator.mAllAppWinAnimators.clear();
                final int windowsCount = wtoken.allAppWindows.size();
                for (int j = 0; j < windowsCount; j++) {
                    appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
                }
                mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();//这个函数会设置VSync回调,并且设置动画
                mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
                ......
            }
            appsCount = mClosingApps.size();//要关闭的app的APPWindowToken
            for (i = 0; i < appsCount; i++) {
                AppWindowToken wtoken = mClosingApps.valueAt(i);
                final AppWindowAnimator appAnimator = wtoken.mAppAnimator;

                appAnimator.clearThumbnail();
                appAnimator.animation = null;
                wtoken.inPendingTransaction = false;
                setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction);//设置动画
                wtoken.updateReportedVisibilityLocked();
                wtoken.allDrawn = true;
                wtoken.deferClearAllDrawn = false;

                if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) {
                    scheduleRemoveStartingWindowLocked(wtoken);
                }
                mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();

                ......
            }

            ......

            mOpeningApps.clear();
            mClosingApps.clear();

            // This has changed the visibility of windows, so perform
            // a new layout to get them all up-to-date.
            changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
                    | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
            ......
        }

        return changes;
    }

这段代码将给所有参与了切换操作的Activity组件设置一个切换动画,而这个动画就来自前面所获得的变量animLp所指向的一个WindowManager.LayoutParams对象。由于参与了切换操作的Activity组件可以划分为两类,即一类是正在打开的,一类是正在打闭的,因此,我们就分别讨论这两种类型的Activity组件的切换动画的设置过程。
       

1)对于正在打开的Activity组件,它们的切换动画的设置过程如下所示:

        1. 找到对应的AppWindowToken对象;

        2. 将对应的AppWindowToken对象的成员变量reportedVisible的值设置为false,表示还没有向ActivityManagerService服务报告过正在打开的Activity组件的可见性;

        3. 将对应的AppWindowToken对象的成员变量inPendingTransaction的值设置为false,表示正在打开的Activity组件不是处于等待执行切换操作的状态了;

        4. 将对应的AppWindowToken对象的成员变量animation的值设置为null,因为接下来要重新这个成员变量的值来描述正在打开的Activity组件的切换动画;

  5. 调用WindowManagerService类的成员函数setTokenVisibilityLocked将正在打开的Activity组件的可见性设置为true,并且给正在打开的Activity组件设置一个切换动画,这个切换动画会保存在对应的AppWindowToken对象的成员变量animation中;

        6. 调用对应的AppWindowToken对象的成员函数updateReportedVisibilityLocked向ActivityManagerService服务报告正在打开的Activity组件的可见性;

        7. 将对应的AppWindowToken对象的成员变量waitingToShow的值设置为false,表示正在打开的Activity组件的窗口不是处于等待显示的状态了;

        8. 调用对应的AppWindowToken对象的成员函数showAllWindowsLocked通知SurfaceFlinger服务将正在打开的Activity组件的窗口设置为可见的。


2)对于正在关闭的Activity组件,它们的切换动画的设置过程如下所示:

        1. 找到对应的AppWindowToken对象;

        2. 将对应的AppWindowToken对象的成员变量inPendingTransaction的值设置为false,表示正在关闭的Activity组件不是处于等待执行切换操作的状态了;

        3. 将对应的AppWindowToken对象的成员变量animation的值设置为null,因为接下来要重新这个成员变量的值来描述正在关闭的Activity组件的切换动画;

        4. 调用WindowManagerService类的成员函数setTokenVisibilityLocked将正在关闭的Activity组件的可见性设置为true,并且给正在关闭的Activity组件设置一个切换动画,这个切换动画会保存在对应的AppWindowToken对象的成员变量animation中;

        5. 调用对应的AppWindowToken对象的成员函数updateReportedVisibilityLocked向ActivityManagerService服务报告正在关闭的Activity组件的可见性;

        6. 将对应的AppWindowToken对象的成员变量waitingToHide的值设置为false,表示正在关闭的Activity组件的窗口不是处于等待隐藏的状态了;

        7. 将对应的AppWindowToken对象的成员变量allDrawn的值设置为true,这样就可以使得前面所设置的切换动画得以执行。

        给所有参与了切换操作的Activity组件都设置了一个切换动画之后,接下来就可以将WindowManagerService类的成员变量mOpeningApps和mClosingApps所描述的两个ArrayList清空了。


最后我们再来看下最后返回的changes,

changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
                    | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;

       由于前面的操作已经导致那些正在打开的Activity组件的窗口由不可见变为可见,即相当于是导致窗口堆栈发生了变化,这时候就需要重新计算各个窗口的大小,以便让各个窗口对自己的UI元素进行重新布局,这是通过将变量changes的值的PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT位设置为1来实现的。
        从前面的分析可以知道,一旦变量changes的值不等于0,WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLockedInner开始的那个do...while循环就会重复执行,也就是会重复执行以下三个操作:

        1. 计算各个窗口的大小,以便让各个窗口可以对自己的UI元素进行布局。

        2. 计算各个窗口接下来要执行的动画。

        3. 执行各个窗口的动画。
至此,我们就分析完成了Activity组件切换操作了。Activity组件的切换操作执行完成之后,参与了切换操作的Activity组件的窗口就会获得一个切换动画。一个窗口的切换动画,与其本身所设置的进入动画,以及其父窗口所设置的动画,一起形成了窗口的显示过程动画。


AppWindowAnimator的showAllWindowsLocked函数

下面我们再来看看AppWindowAnimator的showAllWindowsLocked函数

boolean showAllWindowsLocked() {
        boolean isAnimating = false;
        final int NW = mAllAppWinAnimators.size();
        for (int i=0; i<NW; i++) {
            WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
            winAnimator.performShowLocked();
            isAnimating |= winAnimator.isAnimating();
        }
        return isAnimating;
    }

这个函数就遍历这个AppWindowWinAnimators下所有的WindowStateAnimator,然后调用WindowStateAnimator的performShowLocked函数,这个函数就是显示动画的第一步,设置VSync信号,已经设置动画。当有VSync信号过来时,就会开始播放动画了。