Android WMS动画系统初探(一)
基于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);
}
- 先获取当前需要执行动画的surface
- 用step1的surface创建一个leash,这个流程看样子会递归调用到根节点到DisplayContent中,这里不做深入
- 将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的过渡动画和屏幕旋转动画的相关流程。