WallpaperToken 窗口动画分析
// frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java
private void handleNonAppWindowsInTransition(int transit, int flags) {
if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
&& (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
&& (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
// 创建 Animation
Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
if (anim != null) {
// 执行动画
mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
}
}
}
if (transit == TRANSIT_KEYGUARD_GOING_AWAY
|| transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
mDisplayContent.startKeyguardExitOnNonAppWindows(
transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
}
}
创建 Animation
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
@Override
public Animation createKeyguardWallpaperExit(boolean goingToNotificationShade) {
if (goingToNotificationShade) {
return null;
} else {
return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_wallpaper_exit);
}
}
// frameworks/base/core/res/res/anim/lock_screen_wallpaper_exit.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<alpha
android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/fast_out_linear_in"
android:duration="200"/>
<!-- Empty animation so the animation has same duration as lock_screen_behind_enter animation
-->
<translate android:fromYDelta="0" android:toYDelta="0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/linear"
android:duration="300" />
</set>
执行动画 startWallpaperAnimation
// frameworks/base/services/core/java/com/android/server/wm/WallpaperController.java
class WallpaperWindowToken extends WindowToken {
void startWallpaperAnimation(Animation a) {
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
// 这里的 mWallpaperTokens 列表来自于 addWallpaperToken() 方法
// 获取 WallpaperWindowToken
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
// 执行壁纸窗口动画
token.startAnimation(a);
}
}
private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
DisplayContent dc, boolean ownerCanManageAppTokens) {
super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens);
dc.mWallpaperController.addWallpaperToken(this);
}
void addWallpaperToken(WallpaperWindowToken token) {
mWallpaperTokens.add(token);
}
void removeWallpaperToken(WallpaperWindowToken token) {
mWallpaperTokens.remove(token);
}
}
// frameworks/base/services/core/java/com/android/server/wm/WallpaperWindowToken.java
void startAnimation(Animation anim) {
for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
final WindowState windowState = mChildren.get(ndx);
windowState.startAnimation(anim);
}
}
// 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 (mInsetProvider != null && mInsetProvider.isControllable()) {
return;
}
final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
// 后面会回调 LocalAnimationAdapter
final AnimationAdapter adapter = new LocalAnimationAdapter(
// 后面会回调 WindowAnimationSpec
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
mWmService.mSurfaceAnimationRunner);
startAnimation(getPendingTransaction(), adapter);
commitPendingTransaction();
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
startAnimation(t, adapter, mWinAnimator.mLastHidden);
}
// frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable {
protected final SurfaceAnimator mSurfaceAnimator;
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.make();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
if (DEBUG_ANIM) Slog.v(TAG, "Starting animation on " + this + ": " + anim);
mSurfaceAnimator.startAnimation(t, anim, hidden);
}
}
// frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
class SurfaceAnimator {
private final WindowManagerService mService;
private AnimationAdapter mAnimation;
@VisibleForTesting
SurfaceControl mLeash;
@VisibleForTesting
final Animatable mAnimatable;
private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
@VisibleForTesting
final Runnable mAnimationFinishedCallback;
private boolean mAnimationStartDelayed;
// 构造函数
SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
WindowManagerService service) {
mAnimatable = animatable;
mService = service;
mAnimationFinishedCallback = animationFinishedCallback;
mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
}
// 开始动画
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
final SurfaceControl surface = mAnimatable.getSurfaceControl();
if (surface == null) {
Slog.w(TAG, "Unable to start animation, surface is null or no children.");
cancelAnimation();
return;
}
mLeash = createAnimationLeash(surface, t,mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
mAnimatable.onAnimationLeashCreated(t, mLeash);
if (mAnimationStartDelayed) {
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
}
mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback);
}
}
// frameworks/base/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
() -> finishCallback.onAnimationFinished(this));
}
RunningAnimation 包装了 SurfaceControl和 AnimationSpec, 并提供 动画完成的回调 finishCallback。
// frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
Runnable finishCallback) {
synchronized (mLock) {
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,finishCallback);
mPendingAnimations.put(animationLeash, runningAnim);
if (!mAnimationStartDeferred) {
// 重点,动画执行入口
mChoreographer.postFrameCallback(this::startAnimations);
}
// Some animations (e.g. move animations) require the initial transform to be applied
// immediately.
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
}
// 下面动画更新监听器中也会调用
private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
if (a.mAnimSpec.needsEarlyWakeup()) {
t.setEarlyWakeup();
}
a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
}
真正动画执行生效的地方
// frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
private void startAnimations(long frameTimeNanos) {
synchronized (mLock) {
startPendingAnimationsLocked();
}
mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
}
@GuardedBy("mLock")
private void startPendingAnimationsLocked() {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
startAnimationLocked(mPendingAnimations.valueAt(i));
}
mPendingAnimations.clear();
}
// 根据xml中配置的参数 来设置相关的动画
@GuardedBy("mLock")
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) {
// Post on other thread that we can push final state without jank.
AnimationThread.getHandler().post(a.mFinishCallback);
}
}
}
}
});
a.mAnim = anim;
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());
}
private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
if (a.mAnimSpec.needsEarlyWakeup()) {
t.setEarlyWakeup();
}
a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
}
// frameworks/base/services/core/java/com/android/server/wm/WindowAnimationSpec.java
public class WindowAnimationSpec implements AnimationSpec {
private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
public WindowAnimationSpec(Animation animation, Point position, Rect stackBounds,
boolean canSkipFirstFrame, int stackClipMode, boolean isAppAnimation,
float windowCornerRadius) {
mAnimation = animation; // 传入动画参数
if (position != null) {
mPosition.set(position.x, position.y);
}
mWindowCornerRadius = windowCornerRadius;
mCanSkipFirstFrame = canSkipFirstFrame;
mIsAppAnimation = isAppAnimation;
mStackClipMode = stackClipMode;
if (stackBounds != null) {
mStackBounds.set(stackBounds);
}
}
@Override
public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
// 获取设置参数
final TmpValues tmp = mThreadLocalTmps.get();
tmp.transformation.clear();
// 通过 mAnimation 设置值, mAnimation 在构造函数中传入
mAnimation.getTransformation(currentPlayTime, tmp.transformation);
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 (mStackClipMode == STACK_CLIP_NONE) {
if (tmp.transformation.hasClipRect()) {
t.setWindowCrop(leash, tmp.transformation.getClipRect());
cropSet = true;
}
} else {
mTmpRect.set(mStackBounds);
if (tmp.transformation.hasClipRect()) {
mTmpRect.intersect(tmp.transformation.getClipRect());
}
t.setWindowCrop(leash, mTmpRect);
cropSet = true;
}
// We can only apply rounded corner if a crop is set, as otherwise the value is meaningless,
// since it doesn't have anything it's relative to.
if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) {
t.setCornerRadius(leash, mWindowCornerRadius);
}
}
}
通过 mAnimation 设置 outTransformation 并返回
// frameworks/base/core/java/android/view/animation/Animation.java
public boolean getTransformation(long currentTime, Transformation outTransformation) {
if (mStartTime == -1) {
mStartTime = currentTime;
}
final long startOffset = getStartOffset();
final long duration = mDuration;
float normalizedTime;
if (duration != 0) {
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
} else {
// time is a step-change with a zero duration
normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
}
final boolean expired = normalizedTime >= 1.0f || isCanceled();
mMore = !expired;
if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
if (!mStarted) {
fireAnimationStart();
mStarted = true;
if (NoImagePreloadHolder.USE_CLOSEGUARD) {
guard.open("cancel or detach or getTransformation");
}
}
if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if (mCycleFlip) {
normalizedTime = 1.0f - normalizedTime;
}
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
// 重点
applyTransformation(interpolatedTime, outTransformation);
}
if (expired) {
if (mRepeatCount == mRepeated || isCanceled()) {
if (!mEnded) {
mEnded = true;
guard.close();
fireAnimationEnd();
}
} else {
if (mRepeatCount > 0) {
mRepeated++;
}
if (mRepeatMode == REVERSE) {
mCycleFlip = !mCycleFlip;
}
mStartTime = -1;
mMore = true;
fireAnimationRepeat();
}
}
if (!mMore && mOneMoreTime) {
mOneMoreTime = false;
return true;
}
return mMore;
}
applyTransformation
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mJ71QQhi-1650438479327)(F:/Document/AAA_Framework/WMS%20%E5%88%86%E6%9E%90/WallpaperToken%20%E7%AA%97%E5%8F%A3%E5%8A%A8%E7%94%BB%E5%88%86%E6%9E%90.assets/image-20220419151842136.png)]
以 ScaleAnimation 为例, 其他的动画都是类似的。
// frameworks/base/core/java/android/view/animation/ScaleAnimation.java
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float sx = 1.0f;
float sy = 1.0f;
float scale = getScaleFactor();
if (mFromX != 1.0f || mToX != 1.0f) {
sx = mFromX + ((mToX - mFromX) * interpolatedTime);
}
if (mFromY != 1.0f || mToY != 1.0f) {
sy = mFromY + ((mToY - mFromY) * interpolatedTime);
}
// 设置 t 的 matrix
if (mPivotX == 0 && mPivotY == 0) {
t.getMatrix().setScale(sx, sy);a
} else {
t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
}
}

本文深入探讨了Android 10中WallpaperToken的窗口动画机制,包括创建Animation、startWallpaperAnimation的执行过程,以及如何通过RunningAnimation包装SurfaceControl和AnimationSpec来实现动画效果。关键在于通过mAnimation设置outTransformation并调用applyTransformation,以ScaleAnimation为例展示了动画的工作原理。
1188





