WindowManagerService 窗口动画(7)

WindowManagerService 窗口动画(7)

简述

WMS中有会有操作窗口动画的过程,这个动画的原理本质和app是一样的,就是每一个VSync不断更新窗口的属性(位置,大小,透明度等等)。
我们基于窗口显示的动画,来研究一下这个流程,主要分两个流程,一个是窗口设置以及触发的点,另一个是动画的实现原理。

流程图:
在这里插入图片描述

窗口动画设置

我们先以窗口创建的enter动画为例来看一下窗口动画的配置过程。
我们上一章介绍relayoutWindow的流程中,有提到过在commitFinishDrawingLocked中,我们会调用performShowLocked来把窗口状态设置到HAS_DRAWN,其实这个方法中还不止做了这件事,同时还设置了窗口动画,我们就从这里开始。

1.1 WindowState.performShowLocked
主要调用关联WindowStateAnimator的applyEnterAnimationLocked来配置动画,除此之外还调用了WMS的scheduleAnimationLocked来触发动画启动,动画启动流程我们下一小节介绍,这里主要看动画的配置过程。

boolean performShowLocked() {
    if (!showToCurrentUser()) {
        // ...
        return false;
    }

    final int drawState = mWinAnimator.mDrawState;
    if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
        if (mAttrs.type != TYPE_APPLICATION_STARTING) {
            mActivityRecord.onFirstWindowDrawn(this);
        } else {
            mActivityRecord.onStartingWindowDrawn();
        }
    }
    // 判断窗口之前的状态是否完成
    if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
        return false;
    }

    mWmService.enableScreenIfNeededLocked();
    // 设置动画的方法,详见1.2
    mWinAnimator.applyEnterAnimationLocked();

    // 设置mLastAlpha为-1,下一次调用 prepareSurfaceLocked() 会让窗口显示.
    mWinAnimator.mLastAlpha = -1;
    // 设置窗口状态为HAS_DRAWN
    mWinAnimator.mDrawState = HAS_DRAWN;
    // 触发WMS窗口动画,下一小节来介绍。
    mWmService.scheduleAnimationLocked();

    if (mHidden) {
        mHidden = false;
        final DisplayContent displayContent = getDisplayContent();

        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowState c = mChildren.get(i);
            if (c.mWinAnimator.mSurfaceController != null) {
                // 递归遍历子窗口的performShowLocked
                c.performShowLocked();
                if (displayContent != null) {
                    displayContent.setLayoutNeeded();
                }
            }
        }
    }

    return true;
}

1.2 WindowStateAnimator.applyEnterAnimationLocked
配置了transit动画类型,调用WindowStateAnimator.applyAnimationLocked来加载启动动画。

void applyEnterAnimationLocked() {
    final int transit;
    // 这个值在addWindow流程或者relayoutWindow时候发现窗口从不可见变为可见都会设置为true
    if (mEnterAnimationPending) {
        mEnterAnimationPending = false;
        transit = WindowManagerPolicy.TRANSIT_ENTER;
    } else {
        transit = WindowManagerPolicy.TRANSIT_SHOW;
    }

    // 这里不处理应用程序主窗口动画和壁纸,因为主窗口一般就是Activity,动画由ActivityRecord控制,壁纸则由WallpaperController控制
    if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) {
        // 配置动画,详见1.3
        applyAnimationLocked(transit, true);
    }

    if (mService.mAccessibilityController.hasCallbacks()) {
        mService.mAccessibilityController.onWindowTransition(mWin, transit);
    }
}

1.3 WindowStateAnimator.applyAnimationLocked
根据transit来加载动画,这里加载动画原理和app加载动画是一样的,调用WindowState.startAnimation启动动画。

boolean applyAnimationLocked(int transit, boolean isEntrance) {
    // 如果已经在动画中,并且动画类型相同,直接返回
    if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) {
        return true;
    }
    // ...处理输入法类型窗口
    if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
        mWin.getDisplayContent().adjustForImeIfNeeded();
        if (isEntrance) {
            mWin.setDisplayLayoutNeeded();
            mService.mWindowPlacerLocked.requestTraversal();
        }
    }

    // Insets窗口,动画由Insets control控制
    if (mWin.mControllableInsetProvider != null) { 
        return false;
    }

    if (mWin.mToken.okToAnimate()) {
        // 根据transit加载特定的动画类型动画
        int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
        int attr = -1;
        Animation a = null;
        if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
            if (anim != DisplayPolicy.ANIMATION_NONE) {
                a = AnimationUtils.loadAnimation(mContext, anim);
            }
        } 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_OLD_NONE);
            }
        }

        if (a != null) {
            // 启动动画,详见2.1
            mWin.startAnimation(a);
            mAnimationIsEntrance = isEntrance;
        }
    } else {
        mWin.cancelAnimation();
    }

    return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}

窗口动画流程

2.1 WindowState.startAnimation
这里除了配置anim的一些参数外,主要是构建了一个LocalAnimationAdapter,LocalAnimationAdapter里面有两个关键的变量,分别是WindowAnimationSpec类型和SurfaceAnimationRunner。
前者是在这里创建的,封装了anim以及当前窗口位置,用于管理动画的具体实现。而后者是用于管理动画的进度,实际就是mWmService.mSurfaceAnimationRunner。

void startAnimation(Animation anim) {

    if (mControllableInsetProvider != null) {
        return;
    }

    final DisplayInfo displayInfo = getDisplayInfo();
    // 初始化anim信息,将窗口和屏幕大小传给anim
    anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
            displayInfo.appWidth, displayInfo.appHeight);
    // 设定时长
    anim.restrictDuration(MAX_ANIMATION_DURATION);
    anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
    // 新建LocalAnimationAdapter,里面包含了一个WindowAnimationSpec,而WindowAnimationSpec则记录了动画以及当前窗口位置等。
    // WindowAnimationSpec是用于管理动画的内容的。而LocalAnimationAdapter后续还有一个SurfaceAnimationRunner类型,这里实际就是mWmService.mSurfaceAnimationRunner,用于控制动画进度的。
    // LocalAnimationAdapter则就是封装了这两者,从实现窗口动画
    final AnimationAdapter adapter = new LocalAnimationAdapter(
            new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
                    0 /* windowCornerRadius */),
            mWmService.mSurfaceAnimationRunner);
    // 详见2.2
    startAnimation(getPendingTransaction(), adapter);
    commitPendingTransaction();
}

2.2 WindowContainer.startAnimation
startAnimation有一些列重载,最终会调用到这里,实际是调用mSurfaceAnimator.startAnimation。
mSurfaceAnimator是SurfaceAnimator类型,在WindowContainer构造函数里新建,里面有一些动画相关的Callback,例如动画结束。

void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
        @AnimationType int type,
        @Nullable OnAnimationFinishedCallback animationFinishedCallback,
        @Nullable Runnable animationCancelledCallback,
        @Nullable AnimationAdapter snapshotAnim) {
    // ... 
    // 详见2.3
    mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
            animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}

2.3 SurfaceAnimator.startAnimation
这里会创建一个Leash Surface,用于执行显示动画
调用前面构造的LocalAnimationAdapter的startAnimation方法播放动画。

void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
        @AnimationType int type,
        @Nullable OnAnimationFinishedCallback animationFinishedCallback,
        @Nullable Runnable animationCancelledCallback,
        @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
    cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
    mAnimation = anim;
    mAnimationType = type;
    mSurfaceAnimationFinishedCallback = animationFinishedCallback;
    mAnimationCancelledCallback = animationCancelledCallback;
    final SurfaceControl surface = mAnimatable.getSurfaceControl();
    // 如果窗口没有Surface,直接cancelAnimation,然后return
    if (surface == null) {
        Slog.w(TAG, "Unable to start animation, surface is null or no children.");
        cancelAnimation();
        return;
    }
    mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
    // 创建一个Surface来播放动画。
    if (mLeash == null) {
        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) {
        ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable);
        return;
    }
    // 这里的mAnimation是我们前面构造的LocalAnimationAdapter。详见2.4
    mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
    // 。。。
    if (snapshotAnim != null) {
        mSnapshot = freezer.takeSnapshotForAnimation();
        if (mSnapshot == null) {
            Slog.e(TAG, "No snapshot target to start animation on for " + mAnimatable);
            return;
        }
        mSnapshot.startAnimation(t, snapshotAnim, type);
    }
}

2.4 LocalAnimationAdapter.startAnimation
这里的mAnimator是SurfaceAnimationRunner类型,我们前面提到过LocalAnimationAdapter通过它来管控动画进度。

public void startAnimation(SurfaceControl animationLeash, Transaction t,
        @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
    // 详见2.5
    mAnimator.startAnimation(mSpec, animationLeash, t,
            () -> finishCallback.onAnimationFinished(type, this));
}

2.5 SurfaceAnimationRunner.startAnimation
这里分动画是否需要边缘扩展,如果需要边缘扩展则先操作边缘扩展,等待Transcation.apply完成,继续动画。
最终动画部分逻辑所做的事都是将动画信息存储到mPendingAnimations中,然后在下一个VSync调用startAnimations

void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
        Runnable finishCallback) {
    synchronized (mLock) {
        final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
                finishCallback);
        boolean requiresEdgeExtension = requiresEdgeExtension(a);

        // 如果动画需要边缘扩展
        if (requiresEdgeExtension) {
            final ArrayList<SurfaceControl> extensionSurfaces = new ArrayList<>();
            synchronized (mEdgeExtensionLock) {
                mEdgeExtensions.put(animationLeash, extensionSurfaces);
            }

            mPreProcessingAnimations.put(animationLeash, runningAnim);

            t.addTransactionCommittedListener(mEdgeExtensionExecutor, () -> {
                final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();

                final Transaction edgeExtensionCreationTransaction = new Transaction();
                //  先扩展窗口
                edgeExtendWindow(animationLeash,
                        animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),
                        edgeExtensionCreationTransaction);

                synchronized (mLock) {
                    if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {
                        synchronized (mEdgeExtensionLock) {
                            if (!mEdgeExtensions.isEmpty()) {
                                // 提交扩展窗口操作
                                edgeExtensionCreationTransaction.apply();
                            }
                        }

                        mPreProcessingAnimations.remove(animationLeash);
                        // 将动画存到mPendingAnimations
                        mPendingAnimations.put(animationLeash, runningAnim);
                        if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                            // 下一帧VSync执行startAnimations
                            // 详见2.6
                            mChoreographer.postFrameCallback(this::startAnimations);
                        }
                    }
                }
            });
        }
        // 如果不需要边缘扩展
        if (!requiresEdgeExtension) {
            // 将动画存到mPendingAnimations
            mPendingAnimations.put(animationLeash, runningAnim);
            if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                // 下一帧VSync执行startAnimations
                // 详见2.6
                mChoreographer.postFrameCallback(this::startAnimations);
            }
        }
        // 调用WindowAnimationSpec.apply,详见2.5.1
        applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
    }
}

2.5.1 applyTransformation
我们先来看applyTransformation

private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
    // 详见2.5.2
    a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
}

2.5.2 WindowAnimationSpec.apply
根据入参动画执行时间,计算当前动画对窗口的变化,存储在transformation
然后根据返回的transformation讲对窗口的变化存储在Transaction t中,后续apply给SurfaceFinger做对应的Surface变化。

public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
    final TmpValues tmp = mThreadLocalTmps.get();
    tmp.transformation.clear();
    // 根据currentPlayTime计算当前动画执行的进度,返回transformation,里面封装的对窗口的变化,包括了大小,位置,透明度等等信息。
    mAnimation.getTransformation(currentPlayTime, tmp.transformation);
    // 将变化的信息都存储到t中(Transaction),后续通知SurfaceFlinger做对应的窗口变化。
    tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
    t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
    t.setAlpha(leash, tmp.transformation.getAlpha());

    boolean cropSet = false;
    if (mRootTaskClipMode == ROOT_TASK_CLIP_NONE) {
        if (tmp.transformation.hasClipRect()) {
            final Rect clipRect = tmp.transformation.getClipRect();
            accountForExtension(tmp.transformation, clipRect);
            t.setWindowCrop(leash, clipRect);
            cropSet = true;
        }
    } else {
        mTmpRect.set(mRootTaskBounds);
        if (tmp.transformation.hasClipRect()) {
            mTmpRect.intersect(tmp.transformation.getClipRect());
        }
        accountForExtension(tmp.transformation, mTmpRect);
        t.setWindowCrop(leash, mTmpRect);
        cropSet = true;
    }

    if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) {
        t.setCornerRadius(leash, mWindowCornerRadius);
    }
}

2.6 startAnimations
调用startPendingAnimationsLocked处理Pending动画

private void startAnimations(long frameTimeNanos) {
    synchronized (mLock) {
        if (!mPreProcessingAnimations.isEmpty()) { 
            return;
        }
        // 处理Pending的动画
        startPendingAnimationsLocked();
    }
    mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
}

2.7 startPendingAnimationsLocked
遍历每一个Pending的动画。对每一个东湖调用startAnimationLocked进行执行。

private void startPendingAnimationsLocked() {
    for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
        // 详见2.8
        startAnimationLocked(mPendingAnimations.valueAt(i));
    }
    mPendingAnimations.clear();
}

2.8 startAnimationLocked
这个方法就是通过VauleAnimator来实现动画,VauleAnimator来控制动画进度,在动画Update监听时,在applyTransformation时候传入时间,以及Transcation,RunningAnimation,applyTransformation前面2.5.1介绍过会更新动画,更新Trasncation。

private void startAnimationLocked(RunningAnimation a) {
    final ValueAnimator anim = mAnimatorFactory.makeAnimator();

    // ValueAnimator和app侧使用动画一样。
    anim.overrideDurationScale(1.0f);
    anim.setDuration(a.mAnimSpec.getDuration());
    anim.addUpdateListener(animation -> {
        synchronized (mCancelLock) {
            if (!a.mCancelled) {
                final long duration = anim.getDuration();
                long currentPlayTime = anim.getCurrentPlayTime();
                if (currentPlayTime > duration) {
                    currentPlayTime = duration;
                }
                // 这个方法在2.5.1介绍过
                applyTransformation(a, mFrameTransaction, currentPlayTime);
            }
        }

        // 下一个VSync提交Transcation
        scheduleApplyTransaction();
    });

    anim.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            synchronized (mCancelLock) {
                if (!a.mCancelled) {
                    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;
    mRunningAnimations.put(a.mLeash, a);

    anim.start();
    if (a.mAnimSpec.canSkipFirstFrame()) {
        anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
    }
    anim.doAnimationFrame(mChoreographer.getFrameTime());
}

小结

本节基于窗口可见的动画介绍了一下WMS是怎么实现窗口动画。
窗口添加或者从不可见到可见的时候,会配置触发窗口可见动画。新建了一个LocalAnimationAdapter,里面有一个WindowAinmationSpec和SurfaceAnimationRunner。
WindowAinmationSpec封装了动画内容,里面包含了Animation。
SurfaceAnimationRunner封装了动画进度以及回调控制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值