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 执行远程动画的流程。后续有机会,再补充吧。

相关推荐
AFinalStone43 分钟前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen44 分钟前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 小时前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui
2501_916007471 小时前
苹果商店iOS应用上架费用全面解析:开发者计划与审核费用计算
android·ios·小程序·https·uni-app·iphone·webview
Android打工仔1 小时前
Kotlin 协程源码解析:协程是如何切换线程的?
android·kotlin
mmsx1 小时前
Android GIS系列 内存加锁、SQLite 开事务:矢量数据集双层并发设计
android·性能优化·app
Godikov1 小时前
没有 adb 怎么排查接口问题?给 OkHttp 加一个"永不炸"的文件日志拦截器
android
邪修king2 小时前
Re:Linux 系统篇(三十四):进程间通信开篇Chapter1 —— 匿名管道 pipe 深度拆解与代码实战
android·linux·运维·开发语言
AFinalStone2 小时前
Android7 SystemUI 源码解析(一)全景概览与架构设计
android·systemui
2501_916008893 小时前
如何实现iOS App代码混淆:SwiftShield使用指南
android·ios·小程序·https·uni-app·iphone·webview