前面一篇文章分析了 Transition 的数据化,结果如下
txt
{
id=13
t=OPEN
f=0x0
trk=0
r=[0@Point(0, 0)]
c=[
{
WCT{RemoteToken{21e666b Task{d7726f #39 type=standard A=10206:com.awesome.helloworld}}}
m=OPEN
f=NONE
leash=Surface(name=Task=39)/@0x173bb7c
sb=Rect(0, 0 - 1280, 1840)
eb=Rect(0, 0 - 1280, 1840)
d=0
endFixedRotation=1
},
{
WCT{RemoteToken{b77580e Task{c7b2d2 #1 type=home}}}
m=TO_BACK
f=SHOW_WALLPAPER
leash=Surface(name=Task=1)/@0xb50d72d
sb=Rect(0, 0 - 1280, 1840)
eb=Rect(0, 0 - 1280, 1840)
d=0
}
]
}
然后把数据发送给 WMShell ,让其执行动画,如下
java
// Transitions.java
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
// ...
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
info.getDebugId(), transitionToken, info);
int activeIdx = findByToken(mPendingTransitions, transitionToken);
// ...
// Move from pending to ready
final ActiveTransition active = mPendingTransitions.remove(activeIdx);
// ActiveTransition 保存了 WMCore 传入的执行动画的数据
active.mInfo = info;
active.mStartT = t;
active.mFinishT = finishT;
if (!mReadyDuringSync.isEmpty()) {
} else {
dispatchReady(active);
}
}
根据 transition token ,取出待执行的 ActiveTransition,然后开始对其进行处理
java
// Transitions.java
/**
* Returns true if dispatching succeeded, otherwise false. Dispatching can fail if it is
* blocked by a sync or sleep.
*/
boolean dispatchReady(ActiveTransition active) {
final TransitionInfo info = active.mInfo;
// ...
// 每一个 Transition 都属于一个 Track
final Track track = getOrCreateTrack(info.getTrack());
// Transition 在执行前,需要先保存到 Track 的 ready queue 中
track.mReadyTransitions.add(active);
// ...
// 初始化 Transition 启动状态,例如,visibility/alpha/transforms
setupStartState(active.mInfo, active.mStartT, active.mFinishT);
// ...
// 处理 Track 的 ready queue 中所有 Transition
processReadyQueue(track);
return true;
}
每一个 Transition 都必须属于一个 Track,对于本文分析的案例,此时需要创建一个 Track。
有了 Track 之后,Transition 必须加入到 Track 的 ready queue 中,并对 Transition 进行状态初始化,如下
java
// 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) {
// transition type 是 OPEN
// true
boolean isOpening = isOpeningType(info.getType());
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.hasFlags(FLAGS_IS_NON_APP_WINDOW & ~FLAG_IS_WALLPAPER)) {
}
if (change.hasFlags(FLAG_IS_WALLPAPER) && !ensureWallpaperInTransitions()) {
}
final SurfaceControl leash = change.getLeash();
final int mode = info.getChanges().get(i).getMode();
if (mode == TRANSIT_TO_FRONT) {
}
// 动画目标只有两个 Task,它们都没有做动画的 parent,所以可以独立做动画
// Don't move anything that isn't independent within its parents
if (!TransitionInfo.isIndependent(change, info)) {
}
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
t.show(leash);
t.setMatrix(leash, 1, 0, 0, 1);
if (isOpening
// If this is a transferred starting window, we want it immediately visible.
&& (change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
}
finishT.show(leash);
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
finishT.hide(leash);
} else if (isOpening && mode == TRANSIT_CHANGE) {
}
}
}
根据 Transition 数据,Transition 状态初始化时
- 对于 app Task,mode 为 OPEN,在 start transactin 和 finish transaction 中都会 show 其 leash surface。
- 对于 launcher Task,mode 为 TO_BACK,在 finish transaction 中 show 其 leash surface。
Transition 状态初始化完成后,就开始处理 Track ready queue 中的所有 Transition,如下
java
// Transtions.java
void processReadyQueue(Track track) {
if (track.mReadyTransitions.isEmpty()) {
// ..
}
final ActiveTransition ready = track.mReadyTransitions.get(0);
if (track.mActiveTransition == null) {
// The normal case, just play it.
track.mReadyTransitions.remove(0);
// ready -> active
track.mActiveTransition = ready;
if (ready.mAborted) {
}
// 执行动画
playTransition(ready);
// 本文不涉及 merge transition
// Attempt to merge any more queued-up transitions.
processReadyQueue(track);
return;
}
// ...
}
java
// Transitions.java
private void playTransition(@NonNull ActiveTransition active) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
final var token = active.mToken;
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionStarting(token);
}
// 初始化动画层级
setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
// 首先由 active.mHandler 优先执行动画
// If a handler already chose to run this animation, try delegating to it first.
if (active.mHandler != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
active.mHandler);
boolean consumed = active.mHandler.startAnimation(token, active.mInfo,
active.mStartT, active.mFinishT, (wct) -> onFinish(token, wct));
if (consumed) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
return;
}
}
/// 如果 active.mHandler 不能执行动画,那么分发给其他 TransitionHandler 去尝试执行
// Otherwise give every other handler a chance
active.mHandler = dispatchTransition(token, active.mInfo, active.mStartT,
active.mFinishT, (wct) -> onFinish(token, wct), active.mHandler);
}
在把 Transition 交给 TransitionHandler 执行之前,还会对动画层级进行初始化,如下
java
// T
/**
* Reparents all participants into a shared parent and orders them based on: the global transit
* type, their transit mode, and their destination z-order.
*/
private static void setupAnimHierarchy(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
final int type = info.getType();
// start transaction 中 show transition root surface
for (int i = 0; i < info.getRootCount(); ++i) {
t.show(info.getRoot(i).getLeash());
}
final int numChanges = info.getChanges().size();
// 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();
// 两个动画目标 Task,都没有执行动画的 parent,因此可以独立做动画
// Don't reparent anything that isn't independent within its parents
if (!TransitionInfo.isIndependent(change, info)) {
continue;
}
// false
boolean hasParent = change.getParent() != null;
final TransitionInfo.Root root = TransitionUtil.getRootFor(change, info);
if (!hasParent) {
// start transaction 中,两个 task leash 都 reparent 到 transition root 上
t.reparent(leash, root.getLeash());
// start transaction 中,设置相对于 parent(此时为 transition root) 的偏移坐标
// 此时都是 0
t.setPosition(leash,
change.getStartAbsBounds().left - root.getOffset().x,
change.getStartAbsBounds().top - root.getOffset().y);
}
// 计算 layer,并在 start transaction 中设置 layer
final int layer = calculateAnimLayer(change, i, numChanges, type);
t.setLayer(leash, layer);
}
}
java
// Transitions.java
static int calculateAnimLayer(@NonNull TransitionInfo.Change change, int i,
int numChanges, @WindowManager.TransitionType int transitType) {
// Put animating stuff above this line and put static stuff below it.
final int zSplitLine = numChanges + 1;
// transitionType 此时是 OPEN
// true
final boolean isOpening = isOpeningType(transitType);
// false
final boolean isClosing = isClosingType(transitType);
// Launcher Task 的 mode 为 TRANSIT_TO_BACK
// app Task 的 mode 为 TRANSIT_OPEN
final int mode = change.getMode();
// Ensure wallpapers stay in the back
if (change.hasFlags(FLAG_IS_WALLPAPER) && Flags.ensureWallpaperInTransitions()) {
}
// Put all the OPEN/SHOW on top
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
if (isOpening) {
// put on top
return zSplitLine + numChanges - i;
} else if (isClosing) {
} else {
}
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
if (isOpening) {
// put on bottom and leave visible
return zSplitLine - i;
} else {
}
} else { // CHANGE or other
}
}
根据 Transition 信息,动画层级初始化,如下
- 对于 app Task,它的 mode 为 OPEN,没有 parent。其 leash surface 被 reparent 到 transition root surface,并且在 start transaction 中,对其 layer 更新为 5。
- 对于 launcher Task,mode 为 TO_BACK,没有 parent。其 leash surface 被 reparent 到 transition root surface,并且在 start transaction 中,对其 layer 更新为 2。
那么,现在可以预见,在 start transaction apply 之时,surface 层级结构,是这样的
stateDiagram-v2
TaskDisplayArea_surface --> transition_root_surface
transition_root_surface --> app_task_surface(layer为2)
transition_root_surface --> launcher_task_surface(layer为5)
动画层级初始化完成后,就把 Transition 动画交给 TransitionHandler 执行。对于远程动画,最终是交给 RemoteTransitionHandler 执行,如下
java
// RemoteTransitionHandler.java
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
// ...
// RemoteTransitionHandler 处理 transition request 时,保存过 remote
RemoteTransition pendingRemote = mRequestedRemotes.get(transition);
// ...
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for (#%d) to %s",
info.getDebugId(), pendingRemote);
if (pendingRemote == null) return false;
final RemoteTransition remote = pendingRemote;
// 动画执行完成的回调
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
public void onTransitionFinished(WindowContainerTransaction wct,
SurfaceControl.Transaction sct) {
unhandleDeath(remote.asBinder(), finishCallback);
if (sct != null) {
finishTransaction.merge(sct);
}
mMainExecutor.execute(() -> {
mRequestedRemotes.remove(transition);
finishCallback.onTransitionFinished(wct);
});
}
};
// remote 是远程的,不用复制 start transaction
// If the remote is actually in the same process, then make a copy of parameters since
// remote impls assume that they have to clean-up native references.
final SurfaceControl.Transaction remoteStartT =
copyIfLocal(startTransaction, remote.getRemoteTransition());
final TransitionInfo remoteInfo =
remoteStartT == startTransaction ? info : info.localRemoteCopy();
try {
handleDeath(remote.asBinder(), finishCallback);
// 通知 launcher 执行动画
// 发送的数据有 transition token
remote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
// start transaction 已经通知 binder 调用复制给 launcher 端,因此这里直接 clear
// assume that remote will apply the start transaction.
startTransaction.clear();
Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
} catch (RemoteException e) {
// ...
}
return true;
}
Launcher 启动 app 时,传入了一个 IRemoteTransition Binder 参数,现在就利用这个 Binder 来通知 Launcher 执行远程动画。
由于一直没得到一个机会去研究 Launcher,因此本文也无法展示 Launcher 执行远程动画的流程。后续有机会,再补充吧。