Android WMS动画系统初探(一)

5,612 阅读7分钟

基于AndroidR源码分析

Android WMS动画系统初探(一)
Android WMS动画系统初探(二)
Android WMS动画系统初探(三)

Android 动画原理

Android中动画的工作过程:在某一个时间点,调用getTransformation(),根据mStartTime和mDuration,计算出当前的进度,在根据mInterpolator计算出转换的进度,然后计算出属性的当前值,保存在matrix中。 再调用Matrix.getValues将属性值取出,运用在动画目标上。

Animation 和 Transform

animation_class.png

Animation 在给定了初始状态、结束状态、启动时间与持续时间后,可以为使用者计算其动画目标在任意时刻的变换(Transformation)

子类:TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation

Transformation 描述了一个变换,包含两个分量:透明度和一个二维变换矩阵

Choreographer

无论APP或者系统,都是可以直接向Choreographer注册FrameCallback来实现动画驱动的。

Choreographer 类似 Handler,处理回调的时机为屏幕的垂直同步(VSync)事件到来之时,其处理回调的过程被当作渲染下一帧的工作的一部分

postCallback(int callbackType, Runnable action, Object token)

在下一次 VSync 时执行 action 所指定的操作。
callbackType 的取值:
CALLBACK_INPUT:处理输入事件
CALLBACK_ANIMATION:处理动画事
CALLBACK_TRAVERSAL:处理布局

postCallbackDelayed(int callbackType, Runnable action, Object token, delayMillis)

比 postCallback 增加了一个延迟

postFrameCallback(FrameCallback callback)

在下一次 VSync 时执行 callback 指定的回调。与 postCallback 本质没有太大区别,其回调类型强制为 CALLBACK_ANIMATION。FrameCallback 接口的定义函数为:doFrame(long frameTimeNanos),参数是各纳秒级的时间戳这个函数是为处理动画帧所涉及的postFrameCallbackDelayed(FrameCallback callback, int timeDelayed)比postFrameCallback 增加了一个延迟

WMS的动画系统

窗口动画的本质

对于View动画,动画的目标就是View,而对于窗口来说,动画的目标其实都是Surface,对不同层级的SurfaceControl进行操纵,会产生不同的动画效果。

目标WindowContainer名称举例
WindowState窗口动画Toast的弹出动画、PopupWindow的弹出动画
AppWindowToken过渡动画App从桌面启动的动画
TaskTask动画Recents的动画,PIP动画
DisplayContent全屏动画转屏动画

WMS类结构

WMS_class_structure.png

WMS结构层次

如上图 WMS的结构层次可以简单概括为:
RootWindowContainer -> DisplayContent -> DisplayArea -> Task -> WindowToken -> WindowState

wms_level.png

根据操纵层级的不同我把动画分类为:窗口动画、过渡动画、Task动画、全屏动画等等

窗口动画

窗口动画的启动入口

在DisplayContent的applySurfaceChangesTransaction函数中,会调用每个窗口的WindowStateAnimator#commitFinishDrawingLocked,这个函数是用于处理绘制状态为COMMIT_DRAW_PENDING或READY_TO_SHOW的窗口,因为窗口到了这两个状态才能做窗口动画。

随后会调用WindowState#performShowLocked,并调用WSA的applyEnterAnimationLocked,最后把绘制状态改为HAS_DRAWN。

当进入WSA的applyEnterAnimationLocked之后,后面走的是Surface动画的统一流程,这个我们在后面统一讲。当窗口的状态变成HAS_DRAW后,会在prepareSurface中被show出来,这样子窗口已经变为可见,并开始做动画.

DisplayContent#applySurfaceChangesTransaction

    void applySurfaceChangesTransaction() {

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
        try {
            // 这里会调用WindowState#performShowLocked
            forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        // 这里的流程最终会调用mSurfaceControl.show()真正显示出surface
        prepareSurfaces();
    }

window_animation_entrance.png

在窗口布局(relayout)阶段调用到
WindowStateAnimator#commitFinishDrawingLocked ->
WindowState#performShowLocked ->
WindowStateAnimator#applyEnterAnimationLocked
开启窗口动画流程

WindowStateAnimator#applyAnimationLocked

    boolean applyAnimationLocked(int transit, boolean isEntrance) {
        if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) {
            // If we are trying to apply an animation, but already running
            // an animation of the same type, then just leave that one alone.
            return true;
        }

        // 设置输入法相关动画
        final boolean isImeWindow = mWin.mAttrs.type == TYPE_INPUT_METHOD;
        if (isEntrance && isImeWindow) {
            mWin.getDisplayContent().adjustForImeIfNeeded();
            mWin.setDisplayLayoutNeeded();
            mService.mWindowPlacerLocked.requestTraversal();
        }

        // Only apply an animation if the display isn't frozen.  If it is
        // frozen, there is no reason to animate and it can cause strange
        // artifacts when we unfreeze the display if some different animation
        // is running.
        if (mWin.mToken.okToAnimate()) {
            // 通过DisplayPolicy选择StatusBar或NavigationBar的动画
            int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
            int attr = -1;
            Animation a = null;
            if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
                if (anim != DisplayPolicy.ANIMATION_NONE) {
                    Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation");
                    // 加载动画
                    a = AnimationUtils.loadAnimation(mContext, anim);
                    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                }
            } else {
                // 选择默认动画
                switch (transit) {
                    case WindowManagerPolicy.TRANSIT_ENTER:
                        attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
                        break;
                    case WindowManagerPolicy.TRANSIT_EXIT:
                        attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
                        break;
                    case WindowManagerPolicy.TRANSIT_SHOW:
                        attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
                        break;
                    case WindowManagerPolicy.TRANSIT_HIDE:
                        attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
                        break;
                }
                // 加载动画
                if (attr >= 0) {
                    a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(
                            mWin.mAttrs, attr, TRANSIT_NONE);
                }
            }
            ...
            if (a != null) {
                if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
                // 流程转到WindowState#startAnimation 执行动画
                mWin.startAnimation(a);
                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                mAnimationIsEntrance = isEntrance;
            }
        } else if (!isImeWindow) {
            mWin.cancelAnimation();
        }

        if (!isEntrance && isImeWindow) {
            mWin.getDisplayContent().adjustForImeIfNeeded();
        }

        return mWin.isAnimating(PARENTS);
    }

WindowState#startAnimation

    void startAnimation(Animation anim) {

        // If we are an inset provider, all our animations are driven by the inset client.
        if (mControllableInsetProvider != null) {
            return;
        }

        final DisplayInfo displayInfo = getDisplayInfo();
        // 重置Animation,并设置mInitialized为true
        anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
                displayInfo.appWidth, displayInfo.appHeight);
        // 设置动画最长时间,默认10s
        anim.restrictDuration(MAX_ANIMATION_DURATION);
        // 设置动画scale
        anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
        // 构建LocalAnimationAdapter,封装了WindowAnimationSpec和SurfaceAnimationRunner
        // WindowAnimationSpec中封装了animation、surface位置、stackBounds等信息
        // SurfaceAnimationRunner创建于WMS构建之时,
        final AnimationAdapter adapter = new LocalAnimationAdapter(
                new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
                        0 /* windowCornerRadius */),
                mWmService.mSurfaceAnimationRunner);
        // mSurfaceAnimator.startAnimation
        startAnimation(getPendingTransaction(), adapter);
        // 再次调用WMS.scheduleAnimationLocked()
        commitPendingTransaction();
    }

动画驱动 :SurfaceAnimationRunner

    // com/android/server/wm/WindowManagerService.java
    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
            Supplier<Surface> surfaceFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
        ...
                mSurfaceAnimationRunner = new SurfaceAnimationRunner(mTransactionFactory,
                mPowerManagerInternal);
        ...
    }

    // com/android/server/wm/SurfaceAnimationRunner.java
        SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
            AnimatorFactory animatorFactory, Transaction frameTransaction,
            PowerManagerInternal powerManagerInternal) {
        // 从ThreadLocal取出SF的Choreographer
        mSurfaceAnimationHandler.runWithScissors(() -> mChoreographer = getSfInstance(),
                0 /* timeout */);
        mFrameTransaction = frameTransaction;
        mAnimationHandler = new AnimationHandler();
        mAnimationHandler.setProvider(callbackProvider != null
                ? callbackProvider
                : new SfVsyncFrameCallbackProvider(mChoreographer));
        // factory用于创建SfValueAnimator
        mAnimatorFactory = animatorFactory != null
                ? animatorFactory
                : SfValueAnimator::new;
        mPowerManagerInternal = powerManagerInternal;
    }

    private class SfValueAnimator extends ValueAnimator {

        SfValueAnimator() {
            setFloatValues(0f, 1f);
        }

        @Override
        public AnimationHandler getAnimationHandler() {
            return mAnimationHandler;
        }
    }

什么是Leash

frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java中定义了一个mSurfaceAnimator成员变量

SurfaceAnimator的startAnimation方法中创建Leash,可以通过SurfaceAnimator的类注释了解Leash

/**
 * A class that can run animations on objects that have a set of child surfaces. We do this by
 * reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash
 * gets attached in the surface hierarchy where the the children were attached to. We then hand off
 * the Leash to the component handling the animation, which is specified by the
 * {@link AnimationAdapter}. When the animation is done animating, our callback to finish the
 * animation will be invoked, at which we reparent the children back to the original parent.
 */
class SurfaceAnimator {

这个类可以针对那种存在多个child surface的对象进行动画,在执行动画的过程中会创建一个没有Buffer的Surface---“Leash”,将所有child surface绑定到leash上,leash同时也会绑定到原先这些child surface绑定的位置。然后我们将leash给到AnimationAdapter去执行动画,执行动画结束后会将所有child surface重新绑定到原先的父节点上。

为什么引入Leash可以参考此文:Android P——LockFreeAnimation

SurfaceAnimator#startAnimation

    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
            @AnimationType int type,
            @Nullable OnAnimationFinishedCallback animationFinishedCallback,
            @Nullable SurfaceFreezer freezer) {
        cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
        mAnimation = anim;
        mAnimationType = type;
        mAnimationFinishedCallback = animationFinishedCallback;
        // step1 : 先获取当前需要执行动画的surface
        final SurfaceControl surface = mAnimatable.getSurfaceControl();
        if (surface == null) {
            Slog.w(TAG, "Unable to start animation, surface is null or no children.");
            cancelAnimation();
            return;
        }
        mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
        if (mLeash == null) {
            // step2 : 用step1的surface创建一个leash
            mLeash = createAnimationLeash(mAnimatable, surface, t, type,
                    mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
                    0 /* y */, hidden, mService.mTransactionFactory);
            mAnimatable.onAnimationLeashCreated(t, mLeash);
        }
        mAnimatable.onLeashAnimationStarting(t, mLeash);
        if (mAnimationStartDelayed) {
            if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
            return;
        }
        // step3 : 将leash传给AnimationAdapter,执行动画
        mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
    }

  1. 先获取当前需要执行动画的surface
  2. 用step1的surface创建一个leash,这个流程看样子会递归调用到根节点到DisplayContent中,这里不做深入
  3. 将leash传给AnimationAdapter,执行动画

mAnimation.startAnimation这一步最终会通过LocalAnimationAdapter找到WMS里的SurfaceAnimationRunner进行执行。

这是 WindowContainer与SurfaceAnimtor、SurfaceAnimationRunner的持有关系 :

windowcontainer.png

SurfaceAnimationRunner#startAnimation

    void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
            Runnable finishCallback) {
        synchronized (mLock) {
            // 封装RunningAnimation对象
            final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
                    finishCallback);
            // 加入mPendingAnimations这个ArrayMap
            mPendingAnimations.put(animationLeash, runningAnim);
            if (!mAnimationStartDeferred) {
                // 等待下一次Vsync执行startAnimations(),开始执行动画
                mChoreographer.postFrameCallback(this::startAnimations);
            }

            // 一些动画(例如移动动画)需要立即应用初始变换。
            applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
        }
    }

往编舞者上抛的runnable是执行startAnimations方法

SurfaceAnimationRunner#startAnimations ->
SurfaceAnimationRunner#startPendingAnimationsLocked 会从mPendingAnimations遍历RunningAnimation并执行startAnimationLocked

SurfaceAnimationRunner#startAnimationLocked

    @GuardedBy("mLock")
    private void startAnimationLocked(RunningAnimation a) {
        // 使用AnimationFactory创建一个SfValueAnimator
        final ValueAnimator anim = mAnimatorFactory.makeAnimator();

        // Animation length is already expected to be scaled.
        anim.overrideDurationScale(1.0f);
        anim.setDuration(a.mAnimSpec.getDuration());
        // 实现UpdaterListener处理每一帧动画
        anim.addUpdateListener(animation -> {
            synchronized (mCancelLock) {
                if (!a.mCancelled) {
                    final long duration = anim.getDuration();
                    long currentPlayTime = anim.getCurrentPlayTime();
                    if (currentPlayTime > duration) {
                        currentPlayTime = duration;
                    }
                    // 计算Transformation应用到leash中
                    // 实际执行的是前面封装的WindowAnimationSpec#apply方法
                    // 这里会计算真正要执行的的动画(Transformation)效果
                    // 这一步的目标是为mFrameTransaction设置要执行的事务
                    applyTransformation(a, mFrameTransaction, currentPlayTime);
                }
            }

            // Transaction will be applied in the commit phase.
            // 在下一个Vsync信号到来时,提交动画事务(mFrameTransaction)
            scheduleApplyTransaction();
        });

        // 设置动画开始和完成时的处理
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                synchronized (mCancelLock) {
                    if (!a.mCancelled) {
                        // TODO: change this back to use show instead of alpha when b/138459974 is
                        // fixed.
                        mFrameTransaction.setAlpha(a.mLeash, 1);
                    }
                }
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                synchronized (mLock) {
                    mRunningAnimations.remove(a.mLeash);
                    synchronized (mCancelLock) {
                        if (!a.mCancelled) {
                            // Post on other thread that we can push final state without jank.
                            mAnimationThreadHandler.post(a.mFinishCallback);
                        }
                    }
                }
            }
        });
        a.mAnim = anim;
        // 动画启动前将这个ValueAnimator加入mRunningAnimations这个ArrayMap
        mRunningAnimations.put(a.mLeash, a);
        // 真正开启动画
        anim.start();
        if (a.mAnimSpec.canSkipFirstFrame()) {
            // If we can skip the first frame, we start one frame later.
            anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
        }

        // 通过手动应用动画框架立即启动动画。 否则,开始时间只会在下一个帧中设置,导致延迟。
        anim.doAnimationFrame(mChoreographer.getFrameTime());
    }

这一步构建了一个SfValueAnimator来真正的驱动动画,每一帧的处理是通过WindowAnimationSpec构建真正要执行的动画事务,然后使用mChoreographer.postCallback在下一个vsync信号到来时提交动画事务。 ValueAnimator驱动动画的原理本文就不做深入了。

下一篇文章我将进一步分析Activiy的过渡动画和屏幕旋转动画的相关流程。

过渡动画

Android WMS动画系统初探(二)

屏幕旋转动画

Android WMS动画系统初探(三)