美文网首页
Android WMS动画系统初探(一)

Android WMS动画系统初探(一)

作者: 汪和呆喵 | 来源:发表于2021-10-20 17:50 被阅读0次

    基于AndroidR源码分析

    Android WMS动画系统初探(一)

    Android WMS动画系统初探(二)

    Android WMS动画系统初探(三)

    Android 动画原理

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

    Animation 和 Transform

    [图片上传失败...(image-8c5ae5-1636101404926)]

    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从桌面启动的动画
    Task Task动画 Recents的动画,PIP动画
    DisplayContent 全屏动画 转屏动画

    WMS类结构

    [图片上传失败...(image-b25ef9-1636101404926)]

    WMS结构层次

    如上图 WMS的结构层次可以简单概括为:

    RootWindowContainer -> DisplayContent -> DisplayArea -> Task -> WindowToken -> WindowState

    [图片上传失败...(image-2bdd8a-1636101404926)]

    根据操纵层级的不同我把动画分类为:窗口动画、过渡动画、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();
        }
    

    [图片上传失败...(image-d5ed20-1636101404926)]

    在窗口布局(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的持有关系 :

    [图片上传失败...(image-f741c0-1636101404926)]

    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动画系统初探(三)

    相关文章

      网友评论

          本文标题:Android WMS动画系统初探(一)

          本文链接:https://www.haomeiwen.com/subject/qgotaltx.html