Android app 冷启动(7) 执行动画

前面一篇文章分析了 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 状态初始化时

  1. 对于 app Task,mode 为 OPEN,在 start transactin 和 finish transaction 中都会 show 其 leash surface。
  2. 对于 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 信息,动画层级初始化,如下

  1. 对于 app Task,它的 mode 为 OPEN,没有 parent。其 leash surface 被 reparent 到 transition root surface,并且在 start transaction 中,对其 layer 更新为 5。
  2. 对于 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 执行远程动画的流程。后续有机会,再补充吧。

相关推荐
alexhilton12 分钟前
理解Jetpack Compose中副作用函数的内部原理
android·kotlin·android jetpack
恋猫de小郭4 小时前
腾讯 Kuikly 正式开源,了解一下这个基于 Kotlin 的全平台框架
android·前端·ios
贫道绝缘子4 小时前
【Android】四大组件之Activity
android
人生游戏牛马NPC1号5 小时前
学习Android(四)
android·kotlin
_祝你今天愉快5 小时前
安卓触摸事件分发机制分析
android
fyr897575 小时前
Ubuntu 下编译goldfish内核并使用模拟器运行
android·linux
心之所向,自强不息6 小时前
关于Android Studio的Gradle各项配置
android·ide·gradle·android studio
隐-梵6 小时前
Android studio学习之路(八)---Fragment碎片化页面的使用
android·学习·android studio
百锦再6 小时前
Kotlin学习基础知识大全(上)
android·xml·学习·微信·kotlin·studio·mobile
前期后期6 小时前
Android 智能家居开发:串口是什么,为什么android版本都比较低?粘包半包的原因以及处理思路,缓冲区处理,以及超时清空缓冲区....
android·智能家居