【Android 14源码分析】ShellTransitions-4-播放动画与结束处理

935 阅读6分钟

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。

                        -- 服装学院的IT男

建议阅读顺序:

BLASTSyncEngine设计剖析

ShellTransitions总体流程介绍

ShellTransitions-1-同步组初始化

ShellTransitions-2-requestStartTransition处理

ShellTransitions-3-动画前准备

ShellTransitions-4-播放动画与结束处理

在 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.IWindowContainerTokenStubStubProxy@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.IWindowContainerTokenStubStubProxy@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.IWindowContainerTokenStubStubProxy@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.IWindowContainerTokenStubStubProxy@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}]}

    1. 构建一个 Runnable 动画结束后执行,根据之前的分析最终会执行到 Transitions::onFinish 方法
    1. 构建动画执行体,内部稍后详细分析
    1. 这个 startTransaction 就是参与同步容器的所有事务的合集,在这里 apply ,因为马上要开始动画了
    1. 开始执行动画

根据日志 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件事:

    1. 触发 FinishTransaction 的 apply
    1. 触发第二次 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;
        ......
    }