忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
-- 服装学院的IT男
建议阅读顺序:
ShellTransitions-2-requestStartTransition处理
在 system_server 端调用传递的参数现在都清楚了,再看一下 SystemUI 的 TransitionPlayerImpl 实现。
# com.android.wm.shell.transition.Transitions
private final TransitionPlayerImpl mPlayerImpl;
// Player 服务端
@BinderThread
private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
@Override
public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
throws RemoteException {
// 当前看这
// 第二次 WMCore -> WMShell onTransitionReady
mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
iBinder, transitionInfo, t, finishT));
}
@Override
public void requestStartTransition(IBinder iBinder,
TransitionRequestInfo request) throws RemoteException {
// WMCore -> WMShell requestStartTransition
mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
}
}
方便后续逻辑的理解,再对 TransitionPlayerImpl::onTransitionReady 的几个参数解释一下:
- iBinder : 2端 ActiveTransition 和 Transition 的链接关系,当前在 SystemUI 端可以根据这个 token 获取到 ActiveTransition
- transitionInfo : 里面包含了 system_server 的参数,主要是有动画图层,和目标 Task 的一些信息
- t :同步引擎完成的总事务,代码中也命名成 “StartTransaction” 在 SystemUI 通过这个 SurfaceControl.Transaction 做动画
- finishT :也叫“FinishTransaction” ,动画结束后的一些处理,比如移除"Transition Root: "图层,并将图层信息恢复到动画前的挂载关系
继续看主流程。
Transitions
@VisibleForTesting
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);
int activeIdx = findByToken(mPendingTransitions, transitionToken);
......
// 拿到 WMShell 端对应的 ActiveTransition
final ActiveTransition active = mPendingTransitions.remove(activeIdx);
active.mInfo = info;
// 赋值到 ActiveTransition
active.mStartT = t;
active.mFinishT = finishT;
......
// 正常走这
dispatchReady(active);
......
}
这里日志也会输出进来的 info 。
打印日志: WindowManagerShell: onTransitionReady android.os.BinderProxy@6878cf7: {id=6 t=OPEN f=0x0 trk=0 r=[0@Point(0, 0)] c=[{WCT{android.window.IWindowContainerTokenProxy@4fa59f4} m=OPEN f=NONE leash=Surface(name=Task=7)/@0x5fbfb5c sb=Rect(0, 0 - 720, 1612) eb=Rect(0, 0 - 720, 1612) d=0},{WCT{android.window.IWindowContainerTokenProxy@cb8d51d} m=TO_BACK f=SHOW_WALLPAPER leash=Surface(name=Task=1)/@0x8083e65 sb=Rect(0, 0 - 720, 1612) eb=Rect(0, 0 - 720, 1612) d=0}]}
# Transitions
boolean dispatchReady(ActiveTransition active) {
// 拿到请求信息
final TransitionInfo info = active.mInfo;
...... // 一堆info的解析
// 动画主逻辑
processReadyQueue(track);
return true;
}
# Transitions
void processReadyQueue(Track track) {
......
// 拿到要做动画的 ActiveTransition
final ActiveTransition ready = track.mReadyTransitions.get(0);
......
playTransition(ready);
......
}
# Transitions
private void playTransition(@NonNull ActiveTransition active) {
// 日志,开始播放哪个 ActiveTransition
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
......
// If a handler already chose to run this animation, try delegating to it first.
// 如果之前就有mHandler,则执行开始动画
if (active.mHandler != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
active.mHandler);
......
}
// Otherwise give every other handler a chance
// 之前没有设置 Handler ,则走这里
active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT,
active.mFinishT, (wct, cb) -> onFinish(active, wct, cb), active.mHandler);
}
进入方法就会输出日志
打印日志: WindowManagerShell: Playing animation for (#6)android.os.BinderProxy@6878cf7@0
然后根据第一次 WMCore --> WMShell 时设置的 mHandler 开始播动画,但是如果那一次没有成功设置,则会走最后一行代码来处理动画逻辑。
当前场景为冷启动,走的就是 Transitions::dispatchTransition 逻辑,除了已知的几个参数,还把 Transitions::onFinish 作为动画结束回调传递过去。
# Transitions
TransitionHandler dispatchTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT,
@NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip) {
for (int i = mHandlers.size() - 1; i >= 0; --i) {
if (mHandlers.get(i) == skip) continue;
// 打印日志
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
mHandlers.get(i));
// 播放动画
boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
finishCB);
if (consumed) {
// 如果这个TransitionHandler能成功播放当前类型的动画,则打印日志
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
mHandlers.get(i));
mTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
// 有消费(执行动画)的,则退出循环
return mHandlers.get(i);
}
}
// 一个播放动画的 Handler 都没找到则视为异常
throw new IllegalStateException(
"This shouldn't happen, maybe the default handler is broken.");
}
遍历 mHandlers 下的所有元素,打印日志
打印日志: WindowManagerShell: try handler com.android.wm.shell.transition.DefaultMixedHandler@ef0b845 WindowManagerShell: try handler com.android.wm.shell.keyguard.KeyguardTransitionHandler@a74b0bc WindowManagerShell: try handler com.android.wm.shell.activityembedding.ActivityEmbeddingController@a99f4af WindowManagerShell: try handler com.android.wm.shell.recents.RecentsTransitionHandler@348d48e WindowManagerShell: try handler com.android.wm.shell.pip.PipTransition@793f451 WindowManagerShell: try handler com.android.wm.shell.splitscreen.StageCoordinator@7a66578 WindowManagerShell: try handler com.android.wm.shell.transition.RemoteTransitionHandler@e3f40bf WindowManagerShell: try handler com.android.wm.shell.transition.DefaultTransitionHandler@962f38c
直到遍历到 DefaultTransitionHandler 才停止输出,冷启动场景的动画就是由 DefaultTransitionHandler 播放的。
具体的播放在 DefaultTransitionHandler::startAnimation 完成,并且返回 true ,然后输出日志。
打印日志: WindowManagerShell: animated by com.android.wm.shell.transition.DefaultTransitionHandler@962f38c
其他场景的逻辑也入职,可以根据日志确定具体是由哪个 TransitionHandler 的子类接管了播放动画的逻辑。
现在看看 DefaultTransitionHandler 是如何播放动画的,这里还需要注意参数的传递。
1. 播放默认动画
# DefaultTransitionHandler
@Override
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);
......
// 动画集合
final ArrayList<Animator> animations = new ArrayList<>();
......
// 1. 动画结束回调
final Runnable onAnimFinish = () -> {
// 如果有值则不执行 在 buildSurfaceAnimation 方法里,动画真正结束才执行
if (!animations.isEmpty()) return;
mAnimations.remove(transition);
// 执行传进来的动画结束回调 也就是最终会触发 Transitions::onFinish
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
......
// 遍历Changes
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
......
// 加载动画
Animation a = loadAnimation(info, change, wallpaperTransit, isDreamTransition);
......
// 2. 构建动画
buildSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, change.getEndRelOffset(), cornerRadius,
clipRect);
......
}
......
// 3. 提交总事务
startTransaction.apply();
mAnimExecutor.execute(() -> {
for (int i = 0; i < animations.size(); ++i) {
// 4. 开始动画
animations.get(i).start();
}
});
onAnimFinish.run();
return true;
}
打印日志: WindowManagerShell: start default transition animation, info = {id=6 t=OPEN f=0x0 trk=0 r=[0@Point(0, 0)] c=[{WCT{android.window.IWindowContainerTokenProxy@a7ab550} m=OPEN f=NONE leash=Surface(name=Task=7)/@0x44b7f4d sb=Rect(0, 0 - 720, 1612) eb=Rect(0, 0 - 720, 1612) d=0},{WCT{android.window.IWindowContainerTokenProxy@55dc349} m=TO_BACK f=SHOW_WALLPAPER leash=Surface(name=Task=1)/@0x77cda02 sb=Rect(0, 0 - 720, 1612) eb=Rect(0, 0 - 720, 1612) d=0}]}
-
- 构建一个 Runnable 动画结束后执行,根据之前的分析最终会执行到 Transitions::onFinish 方法
-
- 构建动画执行体,内部稍后详细分析
-
- 这个 startTransaction 就是参与同步容器的所有事务的合集,在这里 apply ,因为马上要开始动画了
-
- 开始执行动画
根据日志 info 里有2个 Changes ,所以会构建2个动画,分别是对新应用 Task ( m=OPEN) 和 桌面(m=TO_BACK)的动画,这里的 m 打印的是 Change 下的 mMode 。
1.1 构建动画
# DefaultTransitionHandler
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);
......
// 动画执行监听,每一帧动画的行为都在 applyTransformation 方法
final ValueAnimator.AnimatorUpdateListener updateListener = animation -> {
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
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);
// 执行毁掉
finishCallback.run();
});
};
va.addListener(new AnimatorListenerAdapter() {
private boolean mFinished = false;
@Override
public void onAnimationEnd(Animator animation) {
// 动画结束
onFinish();
}
......
private void onFinish() {
if (mFinished) return;
// 动画结束
mFinished = true;
finisher.run();
// 手动移除监听,是为了解决bug 252872225.
va.removeUpdateListener(updateListener);
}
});
animations.add(va);
}
动画的每一帧,和动画结束都是执行这个方法,根据参数修改事务的参数,其实有个参数 leash 就是动画图层,也就是"Transition Root: "
# DefaultTransitionHandler
private static void applyTransformation(long time, SurfaceControl.Transaction t,
SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix,
Point position, float cornerRadius, @Nullable Rect immutableClipRect) {
tmpTransformation.clear();
......
t.setMatrix(leash, tmpTransformation.getMatrix(), matrix);
t.setAlpha(leash, tmpTransformation.getAlpha());
......
t.apply();
}
动画具体的执行就不细纠了,知道这里会对 2个 Task 的图层做动画就行,并不像之前想的会对 "Transition Root: "做动画。
动画结束后执行到 Transitions::onFinish
2. 动画结束
动画结束后回调最终会触发 Transitions::onFinish
# Transitions
private void onFinish(ActiveTransition active,
@Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB) {
......
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
+ "(aborted=%b), notifying core %s", active.mAborted, active);
......
// 1. finishTransaction 的处理
SurfaceControl.Transaction fullFinish = active.mFinishT;
if (fullFinish != null) {
// 执行
fullFinish.apply();
}
releaseSurfaces(active.mInfo);
// 2. * WMShell->WMCore finishTransition
mOrganizer.finishTransition(active.mToken, wct, wctCB);
......
}
打印日志: WindowManagerShell: Transition animation finished (aborted=false), notifying core (#5)android.os.BinderProxy@7543f89@0
动画结束后会做2件事:
-
- 触发 FinishTransaction 的 apply
-
- 触发第二次 WMShell->WMCore ,由 system_server 执行动画结束后的逻辑
2.1 system_server 处理(第二次 WMShell --> WMCore)
# WindowOrganizer
public int finishTransition(@NonNull IBinder transitionToken,
@Nullable WindowContainerTransaction t,
@Nullable WindowContainerTransactionCallback callback) {
try {
return getWindowOrganizerController().finishTransition(transitionToken, t,
callback != null ? callback.mInterface : null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
调用到 WindowOrganizerController
# WindowOrganizerController
public int finishTransition(@NonNull IBinder transitionToken,
@Nullable WindowContainerTransaction t,
@Nullable IWindowContainerTransactionCallback callback) {
......
// 拿到 Transition
final Transition transition = Transition.fromBinder(transitionToken);
// DefaultTransitionHandler::buildSurfaceAnimation传递的 WCT 为null
if (t != null) {
......
}
// 结束一个过渡事务
mTransitionController.finishTransition(transition);
......
}
过渡事务结束
# Transition
void finishTransition() {
......
// 置空
mStartTransaction = mFinishTransaction = null;
......
mController.mFinishingTransition = this;
......// 忽略很多逻辑
// 设置过渡事务状态为结束
mState = STATE_FINISHED;
......
}