activity relaunch 会让 Activity 生命周期重新走到 RESUMED,并触发 app 再次绘制。当 WindowManagerService 收到 app 绘制完成时,检测到可以开始执行动画,于是收集 Transition 数据,如下
{
id=13
t=CHANGE
f=0x0
// track id
trk=0
r=[0@Point(0, 0)]
// TransitionInfo#mChanges
c=[
{
WCT{RemoteToken{4220415 Task{d77d5e9 #26 type=standard A=10206:com.awesome.helloworld}}}
m=CHANGE
f=NONE
p=WCT{RemoteToken{fec6a3d DefaultTaskDisplayArea@10996965}}
leash=Surface(name=Task=26)/@0xe4347ff
sb=Rect(0, 0 - 1280, 1840)
eb=Rect(0, 0 - 1840, 1280)
d=0
r=0->1:0
},
{
WCT{RemoteToken{fec6a3d DefaultTaskDisplayArea@10996965}}
m=CHANGE
f=NONE
p=WCT{RemoteToken{3f0e1d2 Display{#0 state=ON size=1840x1280 ROTATION_90}}}
leash=Surface(name=DefaultTaskDisplayArea)/@0x46effc9
sb=Rect(0, 0 - 1280, 1840)
eb=Rect(0, 0 - 1840, 1280)
d=0
r=0->1:-1
},
{
WCT{RemoteToken{3f0e1d2 Display{#0 state=ON size=1840x1280 ROTATION_90}}}
m=CHANGE
f=IS_DISPLAY
leash=Surface(name=WindowedMagnification:0:31)/@0x5399049
sb=Rect(0, 0 - 1280, 1840)
eb=Rect(0, 0 - 1840, 1280)
d=0
// 0->1 代表90°旋转
r=0->1:-1
// 截图层 surface
snapshot=Surface(name=RotationLayer)/@0x48338b1
}
]
}
Wm-Shell 执行动画
有了 Transition 数据后,就发送给 Wm-Shell 来执行动画
// Transitions.java
// 参数 t 是 start transaction
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
transitionToken, info);
// 获取 Transition 数据
final int activeIdx = findByToken(mPendingTransitions, transitionToken);
final ActiveTransition active = mPendingTransitions.remove(activeIdx);
active.mInfo = info;
active.mStartT = t;
active.mFinishT = finishT;
if (!mReadyDuringSync.isEmpty()) {
// ...
} else {
// 分发 transition
dispatchReady(active);
}
}
boolean dispatchReady(ActiveTransition active) {
final TransitionInfo info = active.mInfo;
// ...
// 获取/创建 Track
final Track track = getOrCreateTrack(info.getTrack());
// active 保存到 Track 的 ready queue
track.mReadyTransitions.add(active);
// ...
// 1. 动画状态初始化
setupStartState(active.mInfo, active.mStartT, active.mFinishT);
// ...
// 2. 执行 track ready queue 中的动画
processReadyQueue(track);
return true;
}
1.动画状态初始化
// Transitions.java
/**
* Sets up visibility/alpha/transforms to resemble the starting state of an animation.
*/
private static void setupStartState(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
// ...
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
// ...
final SurfaceControl leash = change.getLeash();
final int mode = info.getChanges().get(i).getMode();
// ...
// 只有 DisplayContent 能独立做动画,因为它没有 parent surface
// Don't move anything that isn't independent within its parents
if (!TransitionInfo.isIndependent(change, info)) {
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
// 对 leash 执行操作
// t 是 start transaction
t.show(leash);
t.setMatrix(leash, 1, 0, 0, 1);
t.setAlpha(leash, 1.f);
t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
}
continue;
}
// ...
}
}
目前,只有 DisplayContent 能独立做动画,这里也只是做了一些基本的操作,没啥用。
2.执行 track ready queue 中的动画
// Transitions.java
void processReadyQueue(Track track) {
// ...
final ActiveTransition ready = track.mReadyTransitions.get(0);
if (track.mActiveTransition == null) {
track.mReadyTransitions.remove(0);
track.mActiveTransition = ready;
// ...
// 执行 transition
playTransition(ready);
// ...
return;
}
// ...
}
private void playTransition(@NonNull ActiveTransition active) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
// ...
// 1.初始化动画层级
setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
// ...
// 2. 分发 Transition
active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT,
active.mFinishT, (wct, cb) -> onFinish(active, wct, cb), active.mHandler);
}
1.初始化动画层级
// Transitions.java
/**
* Reparents all participants into a shared parent and orders them based on: the global transit
* type, their transit mode, and their destination z-order.
*/
// 参数 t 是 start transaction
// finishT 是 finish transaction
private static void setupAnimHierarchy(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
// TRANSIT_CHANGE
final int type = info.getType();
// false
final boolean isOpening = isOpeningType(type);
// false
final boolean isClosing = isClosingType(type);
// 1. start transaction 中 show Transition Root leash
for (int i = 0; i < info.getRootCount(); ++i) {
t.show(info.getRoot(i).getLeash());
}
final int numChanges = info.getChanges().size();
// Put animating stuff above this line and put static stuff below it.
final int zSplitLine = numChanges + 1;
// changes should be ordered top-to-bottom in z
for (int i = numChanges - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final SurfaceControl leash = change.getLeash();
final int mode = change.getMode();
// 不能独立于 parent 做动画的,不执行layer初始化
// 目前只有 DisplayContent 能单独做动画,因为它没有 parent surface
if (!TransitionInfo.isIndependent(change, info)) {
continue;
}
boolean hasParent = change.getParent() != null;
final int rootIdx = TransitionUtil.rootIndexFor(change, info);
// 2. start transaction 中,DisplayContent leash reparent 到 Transition Root lesh
// 并更新 position
if (!hasParent) {
t.reparent(leash, info.getRoot(rootIdx).getLeash());
t.setPosition(leash,
change.getStartAbsBounds().left - info.getRoot(rootIdx).getOffset().x,
change.getStartAbsBounds().top - info.getRoot(rootIdx).getOffset().y);
}
final int layer;
// Put all the OPEN/SHOW on top
if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
// ...
} else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
// ...
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
// ...
} else { // CHANGE or other
if (isClosing || TransitionUtil.isOrderOnly(change)) {
// ...
} else {
// Put above CLOSE mode.
layer = zSplitLine + numChanges - i;
}
}
// 3. start transction 中设置 DisplayContent leash 的 layer
t.setLayer(leash, layer);
}
}
对于屏幕旋转动画来说,由于 DisplayContent 是 root surface,因此 DisplayContent leash 直接 reparent 到 Transition Root leash,并更新 position 和 layer。
而 TaskDisplayArea 和 Task,是不能独立于 parent 做动画的,而 DisplayContent 就是他们的根 parent。因此,只要操作 DisplayContent leash,即可完成屏幕旋转动画。
2.分发Transition
分发 Transition 就是找一个合适的 TransitionHandler 来执行动画,旋转动画由 DefaultTransitionHandler 执行
// DefaultTransitionHandler.java
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"start default transition animation, info = %s", info);
// ...
// 不同的目标,可能执行不同的动画
// animations 用来保存所有需要执行的动画
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
// 任意一个动画执行完成的回调
final Runnable onAnimFinish = () -> {
// 所有动画都执行完毕,才能执行 Transition finished
if (!animations.isEmpty()) return;
mAnimations.remove(transition);
// 4. transition finished
// 回调到 Transition#onFinish(),注意,参数都是为 null
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
// ...
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
// ...
final boolean isTask = change.getTaskInfo() != null;
final int mode = change.getMode();
boolean isSeamlessDisplayChange = false;
// 处理 DisplayContent 旋转
if (mode == TRANSIT_CHANGE && change.hasFlags(FLAG_IS_DISPLAY)) {
if (info.getType() == TRANSIT_CHANGE) {
// 最终解析为 ROTATION_ANIMATION_ROTATE
final int anim = getRotationAnimationHint(change, info, mDisplayController);
// false
isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS;
if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
// 为 DisplayContent 创建旋转动画,并收集到 animations
startRotationAnimation(startTransaction, change, info, anim, animations,
onAnimFinish);
isDisplayRotationAnimationStarted = true;
continue;
}
} else {
// ...
}
}
// ...
// 不能独立于 parent 做动画的,就不单独为他们创建动画
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
// ...
}
// ...
// 2. apply start transaction
startTransaction.apply();
// 3. 在动画线程中执行所有收集来的动画
mAnimExecutor.execute(() -> {
for (int i = 0; i < animations.size(); ++i) {
animations.get(i).start();
}
});
// ...
// run finish now in-case there are no animations
// 立即执行一次动画完成的回调,防止当前没有动画
onAnimFinish.run();
return true;
}
创建屏幕旋转动画
// DefaultTransitionHandler.java
private void startRotationAnimation(SurfaceControl.Transaction startTransaction,
TransitionInfo.Change change, TransitionInfo info, int animHint,
ArrayList<Animator> animations, Runnable onAnimFinish) {
final int rootIdx = TransitionUtil.rootIndexFor(change, info);
// 1.创建 ScreenRotationAnimation
final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext, mSurfaceSession,
mTransactionPool, startTransaction, change, info.getRoot(rootIdx).getLeash(),
animHint);
// The rotation animation may consist of 3 animations: fade-out screenshot, fade-in real
// content, and background color. The item of "animGroup" will be removed if the sub
// animation is finished. Then if the list becomes empty, the rotation animation is done.
final ArrayList<Animator> animGroup = new ArrayList<>(3);
final ArrayList<Animator> animGroupStore = new ArrayList<>(3);
final Runnable finishCallback = () -> {
if (!animGroup.isEmpty()) return;
anim.kill();
animations.removeAll(animGroupStore);
// 所有动画执行完毕,执行 transition finished
onAnimFinish.run();
};
// 2.ScreenRotationAnimation 构建动画
anim.buildAnimation(animGroup, finishCallback, mTransitionAnimationScaleSetting,
mMainExecutor);
// animGroup、animGroupStore、animations 保存的动画都是一样的
for (int i = animGroup.size() - 1; i >= 0; i--) {
final Animator animator = animGroup.get(i);
animGroupStore.add(animator);
animations.add(animator);
}
}
创建 ScreenRotationAnimation
// ScreenRotationAnimation.java
// t 是 start transaction
// animHint 动画类型是 ROTATION_ANIMATION_ROTATE
ScreenRotationAnimation(Context context, SurfaceSession session, TransactionPool pool,
Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash, int animHint) {
mContext = context;
mTransactionPool = pool;
mAnimHint = animHint;
// change 属于 DisplayContent
mSurfaceControl = change.getLeash();
mStartWidth = change.getStartAbsBounds().width();
mStartHeight = change.getStartAbsBounds().height();
mEndWidth = change.getEndAbsBounds().width();
mEndHeight = change.getEndAbsBounds().height();
mStartRotation = change.getStartRotation();
mEndRotation = change.getEndRotation();
// 1. 创建并显示 screenshot leash
mAnimLeash = new SurfaceControl.Builder(session)
// 挂载到 transition root leash 下
.setParent(rootLeash)
.setEffectLayer()
.setCallsite("ShellRotationAnimation")
.setName("Animation leash of screenshot rotation")
.build();
try {
if (change.getSnapshot() != null) {
mScreenshotLayer = change.getSnapshot();
// 把 screenshot surface 挂载到 screenshot leash 下
t.reparent(mScreenshotLayer, mAnimLeash);
mStartLuma = change.getSnapshotLuma();
} else {
// ...
}
// SCREEN_FREEZE_LAYER_BASE 为 10000 * 200 + 10000
t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
t.show(mAnimLeash);
// Crop the real content in case it contains a larger child layer, e.g. wallpaper.
t.setCrop(mSurfaceControl, new Rect(0, 0, mEndWidth, mEndHeight));
// 动画方式为 ROTATION_ANIMATION_CROSSFADE 和 ROTATION_ANIMATION_JUMPCUT 才是自定义动画
if (!isCustomRotate()) {
// 2. 创建并显示 back color surface
mBackColorSurface = new SurfaceControl.Builder(session)
// 挂在 Transition Root leash 下
.setParent(rootLeash)
.setColorLayer()
.setOpaque(true)
.setCallsite("ShellRotationAnimation")
.setName("BackColorSurface")
.build();
// layer 为 -1
t.setLayer(mBackColorSurface, -1);
t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
// start transaction show
t.show(mBackColorSurface);
}
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
// 3. screenshot surface 是竖屏大小,现在要显示在横屏坐标系下,需要对其设置 matrix
setScreenshotTransform(t);
// 4. apply start transaction
t.apply();
}
最后的 apply start transaction 操作,它会如下效果
- 坐标系切换到横屏。
- 最上层显示的是 screenshot surface。 虽然它是竖屏大小,但是由于设置了 matrix,因此它能完整显示在横屏坐标系下,看起来它像一个横屏大小的 surface。
- 最底层显示的 back color surface。
ScreenRotationAnimation 构建动画
// ScreenRotationAnimation.java
boolean buildAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, float animationScale,
@NonNull ShellExecutor mainExecutor) {
if (mScreenshotLayer == null) {
return false;
}
// 自定义旋转动画只有两种 ROTATION_ANIMATION_CROSSFADE 和 ROTATION_ANIMATION_JUMPCUT
final boolean customRotate = isCustomRotate();
if (customRotate) {
// ...
} else {
// Figure out how the screen has moved from the original rotation.
int delta = deltaRotation(mEndRotation, mStartRotation);
switch (delta) { /* Counter-Clockwise Rotations */
// ...
case Surface.ROTATION_270:
// 获取动画资源
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_minus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_minus_90_enter);
break;
}
}
// 初始化退场动画,这是为 screenshot leash 准备的
mRotateExitAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
mRotateExitAnimation.restrictDuration(MAX_ANIMATION_DURATION);
mRotateExitAnimation.scaleCurrentDuration(animationScale);
// 初始化入场动画,这是为 DisplayContent leash 准备的
mRotateEnterAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
mRotateEnterAnimation.restrictDuration(MAX_ANIMATION_DURATION);
mRotateEnterAnimation.scaleCurrentDuration(animationScale);
if (customRotate) {
// ...
} else {
// 1. 为 DisplayContent surface 创建动画
startDisplayRotation(animations, finishCallback, mainExecutor);
// 2. 为 screenshot surface 创建动画
startScreenshotRotationAnimation(animations, finishCallback, mainExecutor);
}
return true;
}
private void startDisplayRotation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
null /* clipRect */);
}
private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
null /* clipRect */);
}
static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Animation anim, @NonNull SurfaceControl leash,
@NonNull Runnable finishCallback, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
@Nullable Rect clipRect) {
final SurfaceControl.Transaction transaction = pool.acquire();
// 构建动画
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
final Transformation transformation = new Transformation();
final float[] matrix = new float[9];
// Animation length is already expected to be scaled.
va.overrideDurationScale(1.0f);
va.setDuration(anim.computeDurationHint());
// 监听动画的每一帧
final ValueAnimator.AnimatorUpdateListener updateListener = animation -> {
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
// 对 surface 进行操作
applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
position, cornerRadius, clipRect);
};
va.addUpdateListener(updateListener);
// 动画完成时的回调
final Runnable finisher = () -> {
applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
position, cornerRadius, clipRect);
pool.release(transaction);
mainExecutor.execute(() -> {
animations.remove(va);
// 执行回调
// 由 DefaultTransitionHanlder 的 finishCallback 实现
finishCallback.run();
});
};
va.addListener(new AnimatorListenerAdapter() {
private boolean mFinished = false;
@Override
public void onAnimationEnd(Animator animation) {
onFinish();
}
@Override
public void onAnimationCancel(Animator animation) {
onFinish();
}
private void onFinish() {
if (mFinished) return;
mFinished = true;
// 动画完成,执行回调
finisher.run();
va.removeUpdateListener(updateListener);
}
});
// 保存动画
animations.add(va);
}
所谓的动画,其实指的就是 ValueAnimator,通过它的每一帧更新时机,来操作 surface(DisplayContent leash 或者 screenshot leash)
// DefaultTransitionHandler.java
private static void applyTransformation(long time, SurfaceControl.Transaction t,
SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix,
Point position, float cornerRadius, @Nullable Rect immutableClipRect) {
// 从动画中获取获取这一帧的 Transformation
tmpTransformation.clear();
anim.getTransformation(time, tmpTransformation);
// ...
// 对 surface 进行操作
t.setMatrix(leash, tmpTransformation.getMatrix(), matrix);
t.setAlpha(leash, tmpTransformation.getAlpha());
// ...
// transactihon 中设置 Vsync id
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
// apply transaction
t.apply();
}
Transition finished
当所有动画都结束时,最终回调 Transitions#onFinish()
// Transitions.java
// 参数都是 null
private void onFinish(ActiveTransition active,
@Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB) {
final Track track = mTracks.get(active.getTrack());
track.mActiveTransition = null;
// ...
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
+ "(aborted=%b), notifying core %s", active.mAborted, active);
// 1. 清理 start transaction
if (active.mStartT != null) {
// Applied by now, so clear immediately to remove any references. Do not set to null
// yet, though, since nullness is used later to disambiguate malformed transitions.
active.mStartT.clear();
}
// 2. apply finish transaction
SurfaceControl.Transaction fullFinish = active.mFinishT;
// ...
if (fullFinish != null) {
fullFinish.apply();
}
releaseSurfaces(active.mInfo);
// 3. 通知 WM-Core finish transition
// 参数 wct 和 wctCB 都是 null
mOrganizer.finishTransition(active.mToken, wct, wctCB);
// ...
}
Wm-Core finish transition
Wm-Core 收到 finish transition,最终会执行 Transition#finishTransition()
// Transition.java
void finishTransition() {
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER) && mIsPlayerEnabled) {
asyncTraceEnd(System.identityHashCode(this));
}
// 记录 finish transition 的日志
mLogger.mFinishTimeNs = SystemClock.elapsedRealtimeNanos();
mController.mLoggerHandler.post(mLogger::logOnFinish);
mController.mTransitionTracer.logFinishedTransition(this);
// 1.清理 ST,FT, CT
if (mStartTransaction != null) mStartTransaction.close();
if (mFinishTransaction != null) mFinishTransaction.close();
mStartTransaction = mFinishTransaction = null;
if (mCleanupTransaction != null) {
mCleanupTransaction.apply();
mCleanupTransaction = null;
}
// 标记正在 finishing 的 transition
mController.mFinishingTransition = this;
if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) {
// ...
}
boolean hasParticipatedDisplay = false;
boolean hasVisibleTransientLaunch = false;
boolean enterAutoPip = false;
boolean committedSomeInvisible = false;
// Commit all going-invisible containers
for (int i = 0; i < mParticipants.size(); ++i) {
final WindowContainer<?> participant = mParticipants.valueAt(i);
// ...
// 旋转动画有 DisplayContent 参与
if (participant.asDisplayContent() != null) {
hasParticipatedDisplay = true;
continue;
}
// ...
// ...
for (int i = 0; i < mTargetDisplays.size(); ++i) {
final DisplayContent dc = mTargetDisplays.get(i);
final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
// 2.通知 AsyncRotationController 动画完成了
if (asyncRotationController != null && containsChangeFor(dc, mTargets)) {
asyncRotationController.onTransitionFinished();
}
// ...
}
// ...
// 3.Transition 状态切换到 STATE_FINISHED
mState = STATE_FINISHED;
// 4. 执行推迟的旋转动画
if (hasParticipatedDisplay && !mController.useShellTransitionsRotation()) {
mController.mAtm.mWindowManager.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
}
// ...
// finishing transition 已经处理完成
mController.mFinishingTransition = null;
}
finish transition 的流程,对于案例分析的屏幕旋转动画来说,没什么太大价值,我这里展示一些关键点,只是为了做点记录。