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封装了动画进度以及回调控制。