启动动效流程梳理(二)

news2024/12/27 6:27:11

performSurfacePlacementNoTrace()

这一段从performSurfacePlacement()开始讲起,因为在trace中可以看到在SystemServer中,动效会从performSurfacePlacement这个tag点触发。这里的流程就是在窗口状态改变之后,会触发performSurfacePlacement流程,在performSurfacePlacement流程中会进行一系列的检查,从而决定是开启动效流程,或者是继续等待,直到动效的前置条件已经就绪再开启动效,具体流程就在这里展开:

void performSurfacePlacement() {
    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
    try {
        performSurfacePlacementNoTrace();
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
}

onSurfacePlacement()

然后在performSurfacePlacementNoTrace()中会触发BLASTSyncEngine中的onSurfacePlacement()方法。

void onSurfacePlacement() {
         if (mActiveSyncs.isEmpty()) return;          (1)
        // queue in-order since we want interdependent syncs to become ready in the same order they
        // started in.
        mTmpFinishQueue.addAll(mActiveSyncs);	
       // There shouldn't be any dependency cycles or duplicates, but add an upper-bound just
        // in case. Assuming absolute worst case, each visit will try and revisit everything
        // before it, so n + (n-1) + (n-2) ... = (n+1)*n/2
         int visitBounds = ((mActiveSyncs.size() + 1) * mActiveSyncs.size()) / 2; //(2)
         while (!mTmpFinishQueue.isEmpty()) {
             if (visitBounds <= 0) {
                 Slog.e(TAG, "Trying to finish more syncs than theoretically possible. This "
                         + "should never happen. Most likely a dependency cycle wasn't detected.");
             }
             --visitBounds;
             final SyncGroup group = mTmpFinishQueue.remove(0); //(3)
             final int grpIdx = mActiveSyncs.indexOf(group);  //(4)
             // Skip if it's already finished:
            if (grpIdx < 0) continue;
            if (!group.tryFinish()) continue;   //(5)
            // Finished, so update dependencies of any prior groups and retry if unblocked.
            int insertAt = 0;
             for (int i = 0; i < mActiveSyncs.size(); ++i) {    //(6)
                 final SyncGroup active = mActiveSyncs.get(i);   //(7)
                if (!active.mDependencies.remove(group)) continue; //(8)
                // Anything afterwards is already in queue.
                if (i >= grpIdx) continue;   //(9)
                if (!active.mDependencies.isEmpty()) continue; //(10)
                 // `active` became unblocked so it can finish, since it started earlier, it should
                 // be checked next to maintain order.
                mTmpFinishQueue.add(insertAt, mActiveSyncs.get(i));  //(11)
                 insertAt += 1;
             }
        }
     }

在onSurfacePlacement()方法中,首先在(1)处检查的是mActiveSyncs是否为空。在代码中找到mActiveSyncs的声明,发现其声明如下,所以对其操作都在该文件内。在本文件中只找到唯一一处向其中添加对象的地方就在startSyncSet方法中。这里可以接上篇博客启动动效流程梳理(一),其中在Transition.java中将mState置为STATE_COLLECTING时,会触发该操作,所以一般从前面的流程走过来的话,这里的mActiveSyncs一般是不为空的。

private final ArrayList<SyncGroup> mActiveSyncs = new ArrayList<>();
void startSyncSet(SyncGroup s, long timeoutMs, boolean parallel) {
    final boolean alreadyRunning = mActiveSyncs.size() > 0;
    if (!parallel && alreadyRunning) {
        // We only support overlapping syncs when explicitly declared `parallel`.
        Slog.e(TAG, "SyncGroup " + s.mSyncId
                + ": Started when there is other active SyncGroup");
    }
    mActiveSyncs.add(s);
    // For now, parallel implies this.
    s.mIgnoreIndirectMembers = parallel;
    ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started %sfor listener: %s",
            s.mSyncId, (parallel && alreadyRunning ? "(in parallel) " : ""), s.mListener);
    scheduleTimeout(s, timeoutMs);
}

在判断完mActiveSyncs是否为空之后,会将其移入mTmpFinishQueue中,然后再将元素从mTmpFinishQueue中依次从头移除,即(3)处所示,然后再在mActiveSyncs中找到该元素对应的索引,正常情况下grpIdx是大于等于0的,因为可以看到mTmpFinishQueue就是mActiveSyncs拷贝过来的,但是在(5)处会进入到tryFinish的流程中,这里可能会移除mActiveSyncs中的group对象,所以不排除这种可能,所以这里的注释就是这个意思。

TryFinish()

然后先进(5)的tryFinish()看下做了什么:
private boolean tryFinish() {
    if (!mReady) return false;
    ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: onSurfacePlacement checking %s",
            mSyncId, mRootMembers);
    if (!mDependencies.isEmpty()) {  //(12)
        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d:  Unfinished dependencies: %s",
                mSyncId, mDependencies);
        return false;
    }
    for (int i = mRootMembers.size() - 1; i >= 0; --i) {  //(13)
        final WindowContainer wc = mRootMembers.valueAt(i);
        if (!wc.isSyncFinished(this)) {
            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d:  Unfinished container: %s",
                    mSyncId, wc);
            return false;
        }
    }
    finishNow();  //(14)
    return true;
}

首先是检查mReady标志位,这里只有在setReady处才会改变mReady的属性,所以也就是说,只有执行setReady(true)后才会执行接下来的流程。然后会检查当前的SyncGroup的mDependencies中的SyncGroup是否为空,而mDependencies的注解是必须在当前SyncGroup之前finish的SyncGroup队列。 根据(12)处的注释解读,mDependencies中的成员,会在finish后被移除队列,所以如果一个SyncGroup在tryFinish时,其mDependencies不为空,就会直接返回失败。 再到(13)处的for循环,这里回去检查当前的SyncGroup的mRootMembers中的各个成员是否绘制完成,如果存在未绘制完成的WindowContainer,则也直接返回false。这里的绘制完成是通过finishDrawing触发,finishDrawing会在应用侧绘制完成时触发,这块前面博客启动动效流程梳理(一)中也讲到过。

FinishNow()

做完上述两个检查才会继续执行到finishNow流程中。finishNow流程的话,简单讲下,首先是创建一个Transaction对象merged,然后再将子节点mRootMembers的mSyncTransaction都合并进merged中。再去执行mRootMembers的mChildren的finishSync方法,这里就看WindowContainer的mChildren是什么类型了,反正基本操作就是将mSyncState置为SYNC_STATE_NONE,且mSyncGroup置为null。之后再将mRootMembers中各个成员及其成员的mChildren都放入wcAwaitingCommit之中。

private void finishNow() {
    if (mTraceName != null) {
        Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, mTraceName, mSyncId);
    }
    ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Finished!", mSyncId);
    SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
    if (mOrphanTransaction != null) {
        merged.merge(mOrphanTransaction);
    }
    for (WindowContainer wc : mRootMembers) {
        wc.finishSync(merged, this, false /* cancel */);
    }

    final ArraySet<WindowContainer> wcAwaitingCommit = new ArraySet<>();
    for (WindowContainer wc : mRootMembers) {
        wc.waitForSyncTransactionCommit(wcAwaitingCommit);
    }
    class CommitCallback implements Runnable {
        // Can run a second time if the action completes after the timeout.
        boolean ran = false;
        public void onCommitted(SurfaceControl.Transaction t) {
            synchronized (mWm.mGlobalLock) {
                if (ran) {
                    return;
                }
                mHandler.removeCallbacks(this);
                ran = true;
                for (WindowContainer wc : wcAwaitingCommit) {
                    wc.onSyncTransactionCommitted(t);
                }
                t.apply();
                wcAwaitingCommit.clear();
            }
        }

        // Called in timeout
        @Override
        public void run() {
            // Sometimes we get a trace, sometimes we get a bugreport without
            // a trace. Since these kind of ANRs can trigger such an issue,
            // try and ensure we will have some visibility in both cases.
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionCommitTimeout");
            Slog.e(TAG, "WM sent Transaction to organized, but never received" +
                    " commit callback. Application ANR likely to follow.");
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            synchronized (mWm.mGlobalLock) {
                onCommitted(merged.mNativeObject != 0
                        ? merged : mWm.mTransactionFactory.get());
            }
        }
    };
    CommitCallback callback = new CommitCallback();
    merged.addTransactionCommittedListener(Runnable::run,
            () -> callback.onCommitted(new SurfaceControl.Transaction())); //(15)
    mHandler.postDelayed(callback, BLAST_TIMEOUT_DURATION);  //(16)

    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
    mListener.onTransactionReady(mSyncId, merged);
    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    mActiveSyncs.remove(this);
    mHandler.removeCallbacks(mOnTimeout);

    // Immediately start the next pending sync-transaction if there is one.
    if (mActiveSyncs.size() == 0 && !mPendingSyncSets.isEmpty()) {
        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "PendingStartTransaction found");
        final PendingSyncSet pt = mPendingSyncSets.remove(0);
        pt.mStartSync.run();
        if (mActiveSyncs.size() == 0) {
            throw new IllegalStateException("Pending Sync Set didn't start a sync.");
        }
        // Post this so that the now-playing transition setup isn't interrupted.
        mHandler.post(() -> {
            synchronized (mWm.mGlobalLock) {
                pt.mApplySync.run();
            }
        });
    }
    // Notify idle listeners
    for (int i = mOnIdleListeners.size() - 1; i >= 0; --i) {
        // If an idle listener adds a sync, though, then stop notifying.
        if (mActiveSyncs.size() > 0) break;
        mOnIdleListeners.get(i).run();
    }
}

在finishNow方法中定义了一个CommitCallback类,其实现了Runnable接口。CommitCallback类中有两个方法,run和onCommitted,主要就是onCommitted方法,这个方法会获取WindowManagerService的大锁,然后遍历wcAwaitingCommit中的成员,并将其mSyncTransaction合并到merged中,然后将merged进行apply()。而callback的onCommitted方法则会在(15)处,于Transaction被从应用侧apply到SF侧的同时调用,这里的机制可以看到上一篇博客WCT系列(五):addTransactionCommittedListener。
在之后就是mListener.onTransactionReady(mSyncId, merged)该方法的调用,这里就是回去播放动效了。该方法需要跳到wm\Transition.java中查看,简单的归纳下该方法的具体作用:
1、 将mState状态修改为STATE_PLAYING;并设置mStartTransaction和mFinishTransaction的状态;
2、 通过动效的mParticipants和mChanges参数计算出mTargets参数;
3、 计算出TransitionInfo类型的info参数的值;
4、 将需要播放的Transition对象从mCollectingTransition列表中移到mPlayingTransition列表中;
5、 发送onTransitionReady Binder到shell线程;
请添加图片描述

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

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

相关文章

js-000000000000

1、js书写的位置 - 内部 <body> <!-- 习惯把 js 放到 /body 的后面 --> <script> console.log(这是内部 js 的书写位置) alert(内部js) </script> </body> <body><!-- 习惯把 js 放到 /body 的后面 --><script>console.log(这…

深度学习工作:从追求 SoTA 到揭示新现象

TLDR&#xff1a;主要讨论了从追求模型 SoTA 到揭示新现象的转变。通过几个例子&#xff0c;包括ACNet到RepVGG的发展&#xff0c;RIFE插帧、Film插帧&#xff0c;以及OpenAI的近期工作&#xff0c;阐述了这种转变的重要性。 知乎&#xff1a;黄哲威 hzwer链接&#xff1a;http…

USB Hub 检测设备

系列文章目录 xHCI 简单分析 USB Root Hub 分析 USB Hub 检测设备 文章目录 系列文章目录一、引言二、hub_eventshub_port_connect_changeusb_alloc_devusb_set_device_statehub_port_initusb_new_device 一、引言 USB Hub 检测设备 一文中讲到&#xff0c;当有 USB 插入时&…

upload-labs关卡记录13

这里和关卡12非常类似&#xff0c;唯一不同就是12关用到get方法&#xff0c;这里用到post方法。因此对应的截断方式也不一样&#xff0c;依旧是使用我们的bp进行抓包&#xff0c; 然后依旧是在upload后加上shell.php&#xff0c;这里用是为了hex时好区别我们要在哪里更改&#…

鸿蒙UI开发——使用WidthTheme实现局部深浅色

1、场景描述 在实际的应用开发中&#xff0c;我们可能需要在界面中局部应用深色或者浅色的界面样式&#xff0c;与全局的深色、亮色同时生效。场景例如&#xff1a;深/亮色预览。此时&#xff0c;我们可以使用WithTheme能力来达到我们的效果。 2、WithTheme WithTheme组件可…

骑砍2霸主MOD开发(26)-Mono脚本系统

一.游戏启动流程 <1.启动器Launcher 运行TaleWorlds.MountAndBlade.Launcher.exe启动C#程序Program,完成MOD列表页面的加载. public class Program {public static void Main(string[] args){#加载启动器GUI界面ResourceDepot resourceDepot new ResourceDepot();resource…

Gmsh有限元网格剖分(Python)---点、直线、平面的移动

Gmsh有限元网格剖分(Python)—点、直线、平面的移动和旋转 最近在学习有限元的网格剖分算法&#xff0c;主要还是要参考老外的开源Gmsh库进行&#xff0c;写一些博客记录下学习过程&#xff0c;方便以后回忆嘞。 Gmsh的官方英文文档可以参考&#xff1a;gmsh.pdf 但咋就说&a…

代码随想录Day39 198.打家劫舍,213.打家劫舍II,337.打家劫舍 III。

1.打家劫舍 力扣题目链接(opens new window) 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统…

01-linux基础命令

一、linux命令概述 命令 选项 参数命令 &#xff1a;就是命令名&#xff1b;选项&#xff1a;一般以 - 开头 比如&#xff1a;-a -p -r 等扩展命令的功能&#xff1b;参数&#xff1a;描述命令的目标&#xff0c;如我们要打开一个文件的话&#xff0c;参数就是要打开的文件名…

MATLAB符号计算-初步认识符号对象

1.1自顶向下的程序设计方法 【例1-1】列主元Guass消去法解方程组 a[2 -3 5 -1;1 4 2 -3;-2 4 -3 -7;8 0 -2 1]; b[3 7 -1 8]; sLZYguass(a,b) 代码还有问题&#xff0c;x11.1913,x21.1157,x30.8114,x40.0923 学长帮忙修改过&#xff1a; 反思&#xff1a; length()、size(…

支持向量机入门指南:从原理到实践

目录 1 支持向量机的基本概念 1.2 数学表达 2 间隔与支持向量 2.1 几何间隔 2.2 支持向量的概念 2.3 规范化超平面 2.4 支持向量的深入分析 2.4.1 支持向量的特征 2.4.2 支持向量的作用 2.4.3 支持向量的代数表示 2.5 KKT条件 3 最优化问题 3.1 问题的形成 3.2 规…

快速汇总Word表格

示例需求&#xff1a;Word文档中的有多个表格&#xff0c;其中最后一个表格为汇总表格&#xff0c;其他的为数据表格&#xff0c;如下图中左侧所示。 现在需要根据Category1和Category2&#xff0c;在数据表格中查找&#xff0c;如果找到匹配行&#xff0c;那么 为数据表中改…

OSCP课后练习-tcpdump

本篇文章旨在为网络安全渗透测试行业OSCP考证教学。通过阅读本文&#xff0c;读者将能够对tcpdump日志分析关键信息过滤有一定了解 1、下载练习分析文件 wget https://www.offensive-security.com/pwk-online/password_cracking_filtered.pcap2、查看分析文件所有内容 sudo t…

Linux快速入门-Linux的常用命令

Linux的常用命令 1. Linux的终端与工作区1.1 终端概述1.2 切换终端 2. Shell语言解释器2.1 Shell概述 3. 用户登录与身份切换3.1 su 命令3.2 sudo 命令 4. 文件、目录操作命令4.1 pwd 命令4.2 cd 命令4.3 ls 命令4.3.1 ls 指令叠加使用 4.4 mkdir 命令4.5 rmdir 命令4.6 cp 命令…

ASP.NET Web应用程序出现Maximum request length exceeded报错

一、问题描述 在ASP.NET的web应用中&#xff0c;导出数据时出现500 - Internal server error.Maximum request length exceeded。 二、原因分析 这个错误通常出现在Web应用程序中&#xff0c;表示客户端发送的HTTP请求的长度超过了服务器配置的最大请求长度限制。这可能是因为…

【JDBC】使用原生JDBC,数据量很大且内存空间较小的情况下,JavaHeap溢出

文章目录 使用原生JDBC&#xff0c;数据量很大且内存空间较小的情况下&#xff0c;JavaHeap非常容易溢出问题背景java.lang.OutOfMemoryError: Java heap space错误分析解决方案1. **优化数据库查询**2. **调整 JVM 堆内存**3. **批量处理数据**4. **线程池优化** 总结**Result…

uniapp 微信小程序 数据空白展示组件

效果图 html <template><view class"nodata"><view class""><image class"nodataimg":src"$publicfun.locaAndHttp()?localUrl:$publicfun.httpUrlImg(httUrl)"mode"aspectFit"></image>&l…

12.26 学习卷积神经网路(CNN)

完全是基于下面这个博客来进行学习的&#xff0c;感谢&#xff01; ​​【深度学习基础】详解Pytorch搭建CNN卷积神经网络LeNet-5实现手写数字识别_pytorch cnn-CSDN博客 基于深度神经网络DNN实现的手写数字识别&#xff0c;将灰度图像转换后的二维数组展平到一维&#xff0c;…

【团标】《信息工程造价政务信息化项目造价评估方法》(TCQAE11021-2023)-费用标准解读系列33

《信息工程造价政务信息化项目造价评估方法》&#xff08;TCQAE11021-2023&#xff09;是中国电子质量管理协会2023年发布&#xff0c;2023年12月16日开始实施的标准&#xff08;了解更多可直接关注我们咨询&#xff09;。该标准适用于政务信息化项目的造价评估&#xff0c;政务…

mybatisplu设置自动填充

/*** mybatisplus的自动化填充*/public class JboltMetaObjectHandler implements MetaObjectHandler {Overridepublic void insertFill(MetaObject metaObject) {LocalDateTime now LocalDateTime.now(ZoneId.of("Asia/Shanghai"));this.strictInsertFill(metaObje…