启动动效流程梳理(一)

news2024/11/14 15:38:17

在这里插入图片描述

因为项目涉及一些启动相关的方案,以及平常处理问题的时候会遇到很多启动响应的问题,所以对启动动效的拉起流程进行一个全面的梳理,同时也借此即会对U版本的动效流程做一个初步的了解吧。
startActivityUnchecked:
启动的流程就先不展开细说了,直接跳到开启启动动效处理的地方。这里直接来到ActivityStarter类的startActivityunchecked方法中:

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        int startFlags, boolean doResume, ActivityOptions options, Task inTask,
        TaskFragment inTaskFragment, boolean restrictedBgActivity,
        NeededUriGrants intentGrants) {
    int result = START_CANCELED;
    final Task startedActivityRootTask;
    final TransitionController transitionController = r.mTransitionController;
    Transition newTransition = (!transitionController.isCollecting()
            && transitionController.getTransitionPlayer() != null)
            ? transitionController.createTransition(TRANSIT_OPEN) : null;   //(1)
    RemoteTransition remoteTransition = r.takeRemoteTransition();//(2)
    try {
        mService.deferWindowLayout();
        transitionController.collect(r);//(3)
        try {
            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
            result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity,
                    intentGrants); //(4)
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
            startedActivityRootTask = handleStartResult(r, options, result, newTransition,
                    remoteTransition); //(5)
        }
    } finally {
        mService.continueWindowLayout();
    }
    postStartActivityProcessing(r, result, startedActivityRootTask);

    return result;
}

在代码(1)处,创建了一个动效的Transition类的对象,这个Transition类表示的是视图树中,从一种状态过渡到另一种状态的过渡动画。也就是说在启动应用的时候需要从桌面打开应用,在这个过程中,应用从桌面图标放大到全屏的一个动画过程就可以理解为一个Transition。
这里首先判断当前是否存在正在收集过程中的Transition对象,然后就是当前是否有能够处理Transition的播放器。然后新建一个处于TRANSIT_OPEN状态的Transtion。

1、 createTransition:

然后就来到创建Transition对象的地方,看看创建一个Transition对象需要做哪些准备。

private Transition createTransition(@WindowManager.TransitionType int type,
        @WindowManager.TransitionFlags int flags) {
    if (mTransitionPlayer == null) { //这里就是前面的getTransitionPlayer的判断
        throw new IllegalStateException("Shell Transitions not enabled");
    }
    if (mCollectingTransition != null) { //其实这里和isCollecting的判断效果是一样的
        throw new IllegalStateException("Simultaneous transition collection not supported"
                + " yet. Use {@link #createPendingTransition} for explicit queueing.");
    }
    Transition transit = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);  //(6)创建新的对象
    ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s", transit);
    moveToCollecting(transit); //(7)
    return transit;
}
Transition(@TransitionType int type, @TransitionFlags int flags,
        TransitionController controller, BLASTSyncEngine syncEngine) {
    mType = type;
    mFlags = flags;
    mController = controller;
    mSyncEngine = syncEngine;
    mToken = new Token(this);

    controller.mTransitionTracer.logState(this);
}

如上代码,其实这里没有太多的处理,就是将前面的条件又重新确认了一遍,然后就开始创建新的Transition对象。这里创建Transition的过程,也很简单,就是将传入的参数赋值给Transition对象的属性。

    void moveToCollecting(@NonNull Transition transition) {
         if (mCollectingTransition != null) {
             throw new IllegalStateException("Simultaneous transition collection not supported.");
         }
         mCollectingTransition = transition;
         // Distinguish change type because the response time is usually expected to be not too long.
         final long timeoutMs =
                 transition.mType == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS;
         mCollectingTransition.startCollecting(timeoutMs);
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start collecting in Transition: %s",
                 mCollectingTransition);
         dispatchLegacyAppTransitionPending();
     }
void startCollecting(long timeoutMs, int method) {
    if (mState != STATE_PENDING) {
        throw new IllegalStateException("Attempting to re-use a transition");
    }
    mState = STATE_COLLECTING;
    mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG, method);

    mController.mTransitionTracer.logState(this);
}

int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name,
        int method) {
    final SyncGroup s = prepareSyncSet(listener, name, method);
    startSyncSet(s, timeoutMs);  //(8)
    return s.mSyncId;
}

void startSyncSet(SyncGroup s, long timeoutMs) {
    if (mActiveSyncs.size() != 0) {
        ProtoLog.w(WM_DEBUG_SYNC_ENGINE,
                "SyncGroup %d: Started when there is other active SyncGroup", s.mSyncId);
    }
    mActiveSyncs.put(s.mSyncId, s);
    ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s",
            s.mSyncId, s.mListener);
    scheduleTimeout(s, timeoutMs);
}

然后就执行到代码(7)处,moveToCollecting()。该方法首先将刚刚新建的transition赋值给TransitionController对象的mCollectingTransition,然后初始化一个timeOutMs。然后就通过这两个属性开始了收集的流程。再进入到startCollecting方法。这个方法就是将transition的mState修改为STATE_COLLECTING(初始状态为STATE_PENDING)。然后再创建一个SyncGroup对象去收集动效相关的WindowContainer,然后又调用了同名的函数startSyncSet方法,这里又将新建的SyncGroup对象放入了mActiveSyncs中,标志着当前是有transition处于激活状态的。
处理完这些,返回刚刚新建的SyncGroup对象的mSyncId,并赋值给新建的transition。于是到这里,第一个功能createTransition执行完毕。

2、 takeRemoteTransition:

takeRemoteTransition方法是ActivityRecord中的方法。这个方法的功能就是返回mPendingRemoteTransition,并再将其置空,我推测,这个mPendingRemoteTransition就像一个消耗品,只能用一次,所以会在用完再将其置空。

RemoteTransition takeRemoteTransition() {
    RemoteTransition out = mPendingRemoteTransition;
    mPendingRemoteTransition = null;
    return out;
}
private void setOptions(@NonNull ActivityOptions options) {
    mLaunchedFromBubble = options.getLaunchedFromBubble();
    mPendingOptions = options;
    if (options.getAnimationType() == ANIM_REMOTE_ANIMATION) {
        mPendingRemoteAnimation = options.getRemoteAnimationAdapter();
    }
    mPendingRemoteTransition = options.getRemoteTransition();
}
void updateOptionsLocked(ActivityOptions options) {
    if (options != null) {
        if (DEBUG_TRANSITION) Slog.i(TAG, "Update options for " + this);
        if (mPendingOptions != null) {
            mPendingOptions.abort();
        }
        setOptions(options);
    }
}

mPendingRemoteTransition的赋值是在setOptions方法中。因为其是私有方法,所以只有在类中调用,但是updateOptionsLocked方法就是一个public方法,这个调用的地方就多了。目前这边的具体调用我还没整理清楚,只知道是从桌面或者SystemUI发送过来的。看其定义就能知道,mPendingRemoteTransition的类型是RemoteTransition,而该类实现了Parcelable,所以必定会跨进程传输的。

3、 collect

在获取到remoteTransition之后,就开始进入收集状态了,这时候需要通过transitionController调用到Transition类的collect方法去收集参与动效的一些窗口了。具体流程进入到代码(3)处。

void collect(@NonNull WindowContainer wc) {
    if (mState < STATE_COLLECTING) {
        throw new IllegalStateException("Transition hasn't started collecting.");
    }
    if (!isCollecting()) {
        // Too late, transition already started playing, so don't collect.
        return;
    }
    ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Collecting in transition %d: %s",
            mSyncId, wc);
    // "snapshot" all parents (as potential promotion targets). Do this before checking
    // if this is already a participant in case it has since been re-parented.
    for (WindowContainer curr = wc.getParent(); curr != null && !mChanges.containsKey(curr);
             curr = curr.getParent()) { //(9)
        mChanges.put(curr, new ChangeInfo(curr));
        if (isReadyGroup(curr)) {//(10)
            mReadyTracker.addGroup(curr); 
            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
                            + " Transition %d with root=%s", mSyncId, curr);
        }
    }
    if (mParticipants.contains(wc)) return;//(11) 若当前wc已经在参与者中,就直接返回了。
    // Wallpaper is like in a static drawn state unless display may have changes, so exclude
    // the case to reduce transition latency waiting for the unchanged wallpaper to redraw.
    final boolean needSyncDraw = !isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent);  //(12)
    if (needSyncDraw) {
        mSyncEngine.addToSyncSet(mSyncId, wc);  //(13)
    }
    ChangeInfo info = mChanges.get(wc);
    if (info == null) {
        info = new ChangeInfo(wc);
        mChanges.put(wc, info);  //(14)
    }
    mParticipants.add(wc);  //(15)
    if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
        mTargetDisplays.add(wc.getDisplayContent());   //(16)
    }
    if (info.mShowWallpaper) {
        // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
        final WindowState wallpaper =
                wc.getDisplayContent().mWallpaperController.getTopVisibleWallpaper();
        if (wallpaper != null) {
            collect(wallpaper.mToken);  //(17)
        }
    }
}

根据上述代码可见,这里的流程首先还是确认下mState的状态,因为在前面给mState已经置为STATE_COLLECTING了,所以这两项异常检查直接跳过。然后来到for循环处(9),在这个循环中,从传入处的wc开始向上遍历其父节点,直到其父节点为空(到顶了)或者mChanges中包含其父节点为止。而for循环中的操作,首先就是将curr放进mChange中。mChange就是一个Map结构,key为WindowContainer,value为ChangeInfo。
代码(10)处的判断就是看curr是不是DisplayContent类的对象,这里可以参见之前的一篇博客,看一下这个窗口的结构:WindowManager相关容器类_taskfrag-CSDN博客。然后再将curr放进ReadyTracker类的成员mReadyGroups中,这个mReadyGroup也是一个MAP,他的key是WindowContainer,value则是一个Boolean类对象。
然后看到(12)处,这里就是将wc放入之前创建的SyncGroup对象中的mRootMembers中,然后再执行prepaerSync(),将当前的wc的所有父节点的mSyncState置为SYNC_STATE_READY,这里为什么父节点直接置为ready,主要是父节点其实只要等子节点置为ready,其就可以置为ready,所以最终还会调用到WindowState类的prepareSync方法。而这一部分其实在这篇博客也详细解释过:WCT系列(四):BLASTSyncEngine-CSDN博客。只有等待对应的窗口触发finishDrawing才可以将WindowState对象的状态置为SYNC_STATE_READY。
到了这里,collect流程也结束了。

4、 handleStartResult:

再次回到startActivityunchecked方法中,因为本次主要梳理动效流程,所以直接看到handleStartResult方法。这个方法也很长,最终重点其实只落在了requestStartTransition这个方法上。

Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask,
        @Nullable RemoteTransition remoteTransition,
        @Nullable TransitionRequestInfo.DisplayChange displayChange) {
    try {
        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                "Requesting StartTransition: %s", transition);
        ActivityManager.RunningTaskInfo info = null;
        if (startTask != null) {
            info = new ActivityManager.RunningTaskInfo();
            startTask.fillTaskInfo(info);
        }
        mTransitionPlayer.requestStartTransition(transition.getToken(),
                new TransitionRequestInfo(transition.mType, info, remoteTransition,
                        displayChange));
        transition.setRemoteTransition(remoteTransition);
    } catch (RemoteException e) {
        Slog.e(TAG, "Error requesting transition", e);
        transition.start();
    }
    return transition;
}

requestStartTransition方法其实就是为了发送一个binder给SystemUI进程,请求开启一个StartTransition,这一步如果平时看trace的话,其实就会很熟悉,在SystemServer进程的startActivityInner这个TAG点下就会有一个异步binder,发往SystemUI进程,请求开启一个startTransition。然后后面SystemServer进程中的onSurfacePlacement这个TAG点下还会发送一个onTransitionReady的binder发往SystemUI,这里就是我们所有的wc的mState都置为SYNC_STATE_READY时会触发的下一个流程了。这里暂时留到下一篇博客讲解。在发送onTransitionReady之后,SystemUI进程就会开启一系列的处理逻辑,最后通过onAnimationStart这个异步binder再通知到桌面开启启动动效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2120694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows10 如何配置python IDE

Windows10 如何配置python IDE 前言Python直接安装&#xff08;快速上手&#xff09;Step1.找到网址Step2.选择版本&#xff08;非常重要&#xff09;Step3. 安装过程Step4. python测试 Anaconda安装&#xff08;推荐&#xff09;Step1. 找到网址Step2. 下载anacondaStep3. 安装…

动手学深度学习(pytorch)学习记录25-汇聚层(池化层)[学习记录]

目录 汇聚层(池化层)&#xff1a;填充和步幅多通道 汇聚层(池化层)&#xff1a; 降低卷积层对位置的敏感性&#xff0c;同时降低对空间降采样表示的敏感性。 汇聚层和卷积层的运动方式一样&#xff0c;从左上角向右下角移动指定步幅&#xff0c;汇聚层执行的是“采样”操作。…

【观察】联想数据网络产品线全新升级,构建高质量的AI网络底座

毫无疑问&#xff0c;数字经济时代&#xff0c;算力已成为推动千行百业数字化转型&#xff0c;赋能经济蓬勃发展的重要引擎&#xff0c;同时也成为衡量国家综合实力的重要指标之一。特别是随着以ChatGPT、GPT4为代表的AI大模型的发布&#xff0c;不仅对数据中心基础设施提出了巨…

ZBrush与Blender雕刻功能哪个更好些?

选择正确的3D软件首先会让你的创作过程更加轻松&#xff0c;尤其是在动画或大片电影制作方面。不同的软件提供不同的功能&#xff0c;并倾向于专注于特定领域&#xff0c;如绘画、动画或雕刻。如果你选择了适合你风格和目标的软件&#xff0c;你可以创作出极具创意的作品。 在…

数据结构:浅谈树的遍历方式

遍历的含义就是把树的所有节点&#xff08;Node&#xff09;按照某种顺序访问一遍。包括前序&#xff0c;中序&#xff0c;后续&#xff0c;广度优先&#xff08;队列&#xff09;&#xff0c;深度优先&#xff08;栈&#xff09;5 种遍历方法。之所以叫前序、中序、后序遍历&a…

python 学习一张图

python学习一张图&#xff0c;python的特点的是学的快&#xff0c;一段时间不用&#xff0c;忘记的也快&#xff0c;弄一张图及一些入门案例吧。 写一个简单的测试&#xff1a; #!/usr/bin/python # -*- coding: UTF-8 -*- import osdef add_num(a, b):return a bif __name__…

【AIGC】探索超凡记忆:SuperMemory,你的私人智能书签助手!

&#x1f9e0; 构建你的第二大脑&#xff1a;SuperMemory 在这个信息爆炸的时代&#xff0c;我们每天都在互联网上浏览和保存大量的信息&#xff0c;但往往这些宝贵的数据就像被扔进了黑洞&#xff0c;再也没有被回顾和利用。 SuperMemory 开源项目应运而生&#xff0c;旨在解…

深度学习基础案例4--运用动态学习率构建CNN卷积神经网络实现的运动鞋识别(测试集的准确率84%)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 前几天一直很忙&#xff0c;一直在数学建模中&#xff0c;没有来得及更新&#xff0c;接下来将恢复正常这一次的案例很有意思&#xff1a;在学习动态调整…

Nexus基本介绍

Nexus是Sonatype公司发布的一款强大的仓库&#xff08;Repository&#xff09;管理软件&#xff0c;常被用来搭建Maven私服&#xff0c;因此也被称为“Maven仓库管理器”。 Nexus 的Web界面&#xff1a; Nexus的主要功能与优势 仓库管理&#xff1a; Nexus支持多种仓库类型…

初识Verilog HDL其一

VerilogHDL是一种硬件描述语言&#xff0c;以文本形式来描述数字系统硬件的结构和行为的语言&#xff0c;用它可以表示逻辑电路图、逻辑表达式&#xff0c;还可以表示数字逻辑系统所完成的逻辑功能。 1.1 Verilog基础语法 1.11 逻辑值 0: 逻辑低电平&#xff0c;条件为假&am…

【信号发生器】连续模式和扫描模式下,示波器呈现出来的波形有何区别

信号发生器在“连续模式”和“扫描模式”下输出的波形在示波器中呈现出的波形形状有显著不同&#xff0c;这与它们各自的工作方式密切相关。 1、连续模式&#xff08;Continuous Mode&#xff09; ①工作方式&#xff1a;信号发生器在连续模式下输出固定频率、固定幅度的信号…

[AHK]编写Dialog类代替内置的InputBox命令

更灵活的自定义的Dialog类&#xff0c;可以用来平替内置的InputBox命令 AHK v1代码 #requires Autohotkey v1.0 answer : Dialog("输出结果&#xff1a;", "good") MsgBox, % "you answered: " answer ExitAppDialog(Question, DefaultValue :…

Java面试篇基础部分-垃圾回收算法

大厂面试垃圾回收算法详解内容。 JVM内存垃圾对象的确定? Java开发过程中,使用引用计数和可达性分析。来确定一个对象是否是需要被回收的对象,如果是需要被回收的对象则,对其进行垃圾回收,如果不是则说明是存在引用的对象,则不能清理 首先来分析一下引用算法,如果这个时…

web防火墙技术措施有哪些?防火墙主要类型有哪些

网络安全已经成为了一个不可忽视的重要议题。web防火墙是入侵检测系统&#xff0c;入侵防御系统的一种。web防火墙技术措施有哪些&#xff1f;Web应用防火墙&#xff0c;正是守护网络安全的一道坚实屏障。今天就跟着小编一起了解下关于web防火墙。 web防火墙技术措施有哪些&…

[C高手编程] const, volatile, register, inline: 修饰符与关键字全面解析

&#x1f496;&#x1f496;⚡️⚡️专栏&#xff1a;C高手编程-面试宝典/技术手册/高手进阶⚡️⚡️&#x1f496;&#x1f496; 「C高手编程」专栏融合了作者十多年的C语言开发经验&#xff0c;汇集了从基础到进阶的关键知识点&#xff0c;是不可多得的知识宝典。如果你是即将…

内存管控:内存池篇

目录 1.内存池的工作原理 2.内存池的优势 3.长时间运行的应用和内存碎片 4.代码实践 4.1核心结构&#xff08;内存池结构&#xff09; 4.2核心函数 4.2.1创建内存池 4.2.2销毁内存池 4.2.3分配内存块 4.2.4归还内存块入池 内存池&#xff08;Memory Pool&#xff09…

CMake导入OpenCV检测人脸

基于级联分类器的人脸检测示例"face_detection.cpp"&#xff1a; #include <opencv2/opencv.hpp>int main(int argc,char *argv[]) {// load face classifiercv::CascadeClassifier faceCascade;faceCascade.load(argv[3]);cv::Mat image cv::imread(argv[1]…

Effective Java 学习笔记--42-44 函数接口及其应用(Lambda与方法引用)

目录 函数接口 标准函数接口 自定义函数接口 函数接口的应用 Lambda表达式 Lambda表达式相比于匿名类的优点 方法引用 函数式接口是只包含了一个抽象方法的接口&#xff0c;这种接口的设计目的是为了支持Lambda表达式为代表的函数输入&#xff0c;在Java中有43个标准的默…

基于SpringBoot的旅游网站系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于JavaSpringBootVueMySQL的旅游网站系统【附源码文档】、…

远程连接Hiveserver2服务

目录 1.修改 core-site.xml 和 hive-site.xml 的配置文件 2.启动HiveServer2服务 3.启动Beeline工具连接Hiveserver2服务 4.利用IDEA工具连接Hiveserver2服务 完成Hive本地模式安装后&#xff0c;可以启动hiveserver2服务进行远程连接和操作Hive。 1.修改 core-site.xml …