
昨天又出去面试了,面试官问我 动画这部分你知道么? 我说知道,不就是常见的帧动画,属性动画那堆么... 面试官说我问你WMS
里面的window
的动画原理.. 我说emmm...,又回家等消息了。

咋现在面试那么难。 开始面向AI学习。(源码是aosp13
)
日常我们调用WindowManager
添加一个view
带动画,基本如下写法(我还特地查了下)
java
val v = View(this)
mWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager;
//省略...
val mLayoutParams = WindowManager.LayoutParams();
//省略...
mLayoutParams.windowAnimations = R.style.window_anim;
mWindowManager.addView(tv, mLayoutParams)
xml
<style name="window_anim">
<item name="android:windowEnterAnimation">@anim/window_enter</item>
<item name="android:windowExitAnimation">@anim/window_exit</item>
</style>
我就好奇这动画咋加进去的。
在WindowState
的 performShowLocked
这里
java
mWmService.enableScreenIfNeededLocked();
mWinAnimator.applyEnterAnimationLocked();
mWinAnimator.mLastAlpha = -1;
ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
//这里是HAS_DRAWN
mWinAnimator.mDrawState = HAS_DRAWN;
mWmService.scheduleAnimationLocked();
代码大体流程
js
WindowStateAnimator.applyEnterAnimationLocked()
//这里壁纸是无法进入的在该代码外面有一圈判断 if(mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper)
//1.1
--> applyAnimationLocked(transit, true);
--> mWin.startAnimation(a); //这里的动画adapter 是LocalAnimationAdapter
-->WindowState.startAnimation()
-->WindowContainer.startAnimation()
--> mSurfaceAnimator.startAnimation(t, anim, hidden, type,animationFinishedCallback, mSurfaceFreezer);
//1.3
-->SurfaceAnimator.startAnimation()
java
void applyEnterAnimationLocked() {
//省略....
public static final int TRANSIT_SHOW = 3;
public static final int TRANSIT_ENTER = 1;
final int transit;
if (mEnterAnimationPending) {
mEnterAnimationPending = false;
transit = WindowManagerPolicy.TRANSIT_ENTER;
} else {
transit = WindowManagerPolicy.TRANSIT_SHOW;
}
//省略
if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) {
applyAnimationLocked(transit, true);
}
if (mService.mAccessibilityController.hasCallbacks()) {
mService.mAccessibilityController.onWindowTransition(mWin, transit);
}
}
也就是说默认传到applyAnimationLocked
里面的 值 不是TRANSIT_SHOW
就是TRANSIT_ENTER
这里我打log
启动Activity
的时候, 看了下是TRANSIT_SHOW
然后是 TRANSIT_ENTER
1.1 WindowStateAnimator.applyAnimationLocked()
java
//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
boolean applyAnimationLocked(int transit, boolean isEntrance) {
if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) {
return true;
}
final boolean isImeWindow = mWin.mAttrs.type == TYPE_INPUT_METHOD;
if (isEntrance && isImeWindow) {
mWin.getDisplayContent().adjustForImeIfNeeded();
mWin.setDisplayLayoutNeeded();
mService.mWindowPlacerLocked.requestTraversal();
}
if (mWin.mToken.okToAnimate()) {
// 1.2DisplayPolicy.selectAnimation 默认返回0了
int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
int attr = -1;
Animation a = null;
if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
//不等于0 就去查找对应的动画
if (anim != DisplayPolicy.ANIMATION_NONE) {
a = AnimationUtils.loadAnimation(mContext, anim);
}
} else {
switch (transit) {
//我们是0 就去加载对应的动画
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) {//不为null 执行 如果我们不设置动画 那么这里就是null
//这里属性主要是 layoutParams.windowAnimations
mWin.startAnimation(a);
mAnimationIsEntrance = isEntrance;
}
} else if (!isImeWindow) {
mWin.cancelAnimation();
}
if (!isEntrance && isImeWindow) {
mWin.getDisplayContent().adjustForImeIfNeeded();
}
return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}
稍微总结一下,其实这里就是看我们有没有给window
设置动画,如果有的话就会去我们配置的windowEnterAnimation
/windowExitAnimation
/windowShowAnimation
等去匹配对应的动画值。
1.2 DisplayPolicy.selectAnimation()
java
int selectAnimation(WindowState win, int transit) {
ProtoLog.i(WM_DEBUG_ANIM, "selectAnimation in %s: transit=%d", win, transit);
if (transit == TRANSIT_PREVIEW_DONE) {
if (win.hasAppShownWindows()) {
if (win.isActivityTypeHome()) {
// Dismiss the starting window as soon as possible to avoid the crossfade out
// with old content because home is easier to have different UI states.
return ANIMATION_NONE;
}
ProtoLog.i(WM_DEBUG_ANIM, "**** STARTING EXIT");
return R.anim.app_starting_exit;
}
}
return ANIMATION_STYLEABLE;
}
这里匹配不到 默认返回 ANIMATION_STYLEABLE
为0 则进行进行匹配。
1.3 WindowState.startAnimation()
java
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
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();
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
mWmService.mSurfaceAnimationRunner);
startAnimation(getPendingTransaction(), adapter);
commitPendingTransaction();
}
将我们查到的动画 ,然后 构建了一个LocalAnimationAdapter 然后调用了 startAnimation 继续调用来到了
1.4 WindowContainer.startAnimation()
java
//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim) {
ProtoLog.v(WM_DEBUG_ANIM, "Starting animation on %s: type=%d, anim=%s",
this, type, anim);
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}
1.5 SurfaceAnimator.startAnimation()
java
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();
//省略...
mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
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);
//省略...
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
//省略...
}
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
Transaction t, @AnimationType int type, int width, int height, int x, int y,
boolean hidden, Supplier<Transaction> transactionFactory) {
final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
.setParent(animatable.getAnimationLeashParent())
.setName(surface + " - animation-leash of " + animationTypeToString(type))
.setHidden(hidden)
.setEffectLayer()
.setCallsite("SurfaceAnimator.createAnimationLeash");
final SurfaceControl leash = builder.build();
t.setWindowCrop(leash, width, height);
t.setPosition(leash, x, y);
t.show(leash);
t.setAlpha(leash, hidden ? 0 : 1);
t.reparent(surface, leash);
return leash;
}
这里有个 mAnimatable
是 在
WindowContainer
的构造函数了 将自己传入 构建了 SurfaceAnimator
中的 mAnimatable
。 构建了一个SurfaceControl
, 然后调用了reparent
切换了surface
的为leash
。 其实也就是把当前容器挂载到了mLeash
下。 然后mAnimation
调用了 startAnimation
使用了mLeash
。 并 mInnerAnimationFinishedCallback
当做参数传了出去。
java
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mTransitionController = mWmService.mAtmService.getTransitionController();
mPendingTransaction = wms.mTransactionFactory.get();
mSyncTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
mSurfaceFreezer = new SurfaceFreezer(this, wms);
}
1.6 LocalAnimationAdapter.startAnimation()
java
//frameworks/base/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
() -> finishCallback.onAnimationFinished(type, this));
}
1.7 SurfaceAnimationRunner. startAnimation()
java
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.put(animationLeash, runningAnim);
if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
}
}
});
}
if (!requiresEdgeExtension) {
mPendingAnimations.put(animationLeash, runningAnim);
if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
}
}
这部分代码我结合AI
总结一下, 提交事务,然后下一帧的时候 会回调 startAnimations
, 会进入 SurfaceAnimationRunner.startAnimationLocked()
执行动画的代码了。
小总结一下
总结了一下, 如果我们给window
设置了动画,他会根据进入的状态,去查找是否设置了动画,如果设置了动画,就会去加载对应的动画,然后构建一个SurfaceControl
也就是mLeash
, 然后将当前容器挂载到mLeash
上,去执行动画。 为什么要构建mLeash
,我查了一下,主要图省事,因为如果挂载在真的parent
上去执行, 如果有其他child
都会收到影响,但是单独构建一个SurfaceControl
等动画结束,再挂回去就行了。在12开始有的mLeash
。
还是看一下执行完动画 SurfaceAnimationRunner.startAnimationLocked()
java
private void startAnimationLocked(RunningAnimation a) {
final ValueAnimator anim = mAnimatorFactory.makeAnimator();
// Animation length is already expected to be scaled.
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;
}
applyTransformation(a, mFrameTransaction, currentPlayTime);
}
}
// Transaction will be applied in the commit phase.
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) {
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());
}
在 onAnimationEnd
中会 回调 a.mFinishCallback
其实最终会回到 SurfaceAnimator.mInnerAnimationFinishedCallback
,因为是一路传递过来的。
java
private OnAnimationFinishedCallback getFinishedCallback(
@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback) {
return (type, anim) -> {
synchronized (mService.mGlobalLock) {
final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
if (target != null) {
target.mInnerAnimationFinishedCallback.onAnimationFinished(type, anim);
return;
}
if (anim != mAnimation) {
return;
}
final Runnable resetAndInvokeFinish = () -> {
//省略
final OnAnimationFinishedCallback animationFinishCallback =
mSurfaceAnimationFinishedCallback;
reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);
if (staticAnimationFinishedCallback != null) {
staticAnimationFinishedCallback.onAnimationFinished(type, anim);
}
if (animationFinishCallback != null) {
animationFinishCallback.onAnimationFinished(type, anim);
}
};
if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)
|| anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {
resetAndInvokeFinish.run();
}
mAnimationFinished = true;
}
};
}
private void reset(Transaction t, boolean destroyLeash) {
mService.mAnimationTransferMap.remove(mAnimation);
mAnimation = null;
mSurfaceAnimationFinishedCallback = null;
mAnimationType = ANIMATION_TYPE_NONE;
final SurfaceFreezer.Snapshot snapshot = mSnapshot;
mSnapshot = null;
if (snapshot != null) {
// Reset the mSnapshot reference before calling the callback to prevent circular reset.
snapshot.cancelAnimation(t, !destroyLeash);
}
if (mLeash == null) {
return;
}
SurfaceControl leash = mLeash;
mLeash = null;
final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
mAnimationFinished = false;
if (scheduleAnim) {
mService.scheduleAnimationLocked();
}
}
static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,
boolean destroy) {
boolean scheduleAnim = false;
final SurfaceControl surface = animatable.getSurfaceControl();
//找parent
final SurfaceControl parent = animatable.getParentSurfaceControl();
final SurfaceControl curAnimationLeash = animatable.getAnimationLeash();
final boolean reparent = surface != null && (curAnimationLeash == null
|| curAnimationLeash.equals(leash));
if (reparent) {
//省略...
if (surface.isValid() && parent != null && parent.isValid()) {
t.reparent(surface, parent);
scheduleAnim = true;
}
}
if (destroy) {
t.remove(leash);
scheduleAnim = true;
}
if (reparent) {
animatable.onAnimationLeashLost(t);
scheduleAnim = true;
}
return scheduleAnim;
}
这里主要是动画结束回调,然后 调用到了reset()
,将又调用了t.reparent(surface, parent)
; 又把surface
挂回去了。然后删除了mLeash
。

简单总结,如果我们写了动画,系统会根据状态去匹配,然后构建mLeash
,切换 parent
挂载到mLeash
去执行动画,结束会删除 mLeash
,然后将原有的window
挂回原来的parent
。