Anrdoid WMS动画系统初探(一)
基于AndroidR源码分析
Android 动画原理
Android中动画的工作过程:在某一个时间点,调用getTransformation(),根据mStartTime和mDuration,计算出当前的进度,在根据mInterpolator计算出转换的进度,然后计算出属性的当前值,保存在matrix中。 再调用Matrix.getValues将属性值取出,运用在动画目标上。
Animation 和 Transform
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类结构
WMS结构层次
如上图 WMS的结构层次可以简单概括为:
RootWindowContainer -> DisplayContent -> DisplayArea -> Task -> WindowToken -> WindowState
根据操纵层级的不同我把动画分类为:窗口动画、过渡动画、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(); }复制代码
在窗口布局(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); }复制代码
先获取当前需要执行动画的surface
用step1的surface创建一个leash,这个流程看样子会递归调用到根节点到DisplayContent中,这里不做深入
将leash传给AnimationAdapter,执行动画
mAnimation.startAnimation这一步最终会通过LocalAnimationAdapter找到WMS里的SurfaceAnimationRunner进行执行。
这是 WindowContainer与SurfaceAnimtor、SurfaceAnimationRunner的持有关系 :
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驱动动画的原理本文就不做深入了。
作者:汪和呆喵
链接:https://juejin.cn/post/7021082548985921567