Compose原理 - 整体架构与主流程

news2025/6/3 12:04:28

一、整体架构

官方文档Jetpack Compose 架构层  |  Android DevelopersCompose分层有所阐述:

其中

Runtime提供Compose基础运行能力包括StateSide-effectsCompositionLocal、Composition等等相关API

UI涉及到绘制部分,主要是LayoutNode,Modifiler相关

Foundation提供ColumnRow“设计系统无关”组件

Matieria提供Material Design相关能力比如主题ColorStyle

其中Runtime主要包含CompositionComposition, Recomposer, ComposeNode, RecomposeScope主要组件。除此之外,还有State相关的Snapshot的部分。这些是Compose得以实现核心主要阐述也就这部分架构

Compose源码架构

Jatpack Compose工具包形式集成Android绘制体系所以本质嵌入DecorViewContent换言之Compose组织内容最终绘制ComposeView再将ComposeView添加DecorView如此Compose便集成到AndroidView体系

上图几个重要

Composition可视为Composer容器主要负责与外部其他角色进行对接内部包含了一个Composer。如上图,一般情况下,一个DecorView包含一个Composition,即一个Activity对应一个Composition。如果使用TabRowBoxWithConstraints组件内部调用SubcomposeLayout从而创建多个Composition实例

Composer代表一个Compose内部构建重组等一个整体调度管理

SlotTable可以理解为实现Compose具体数据结构其内部主要两个数组成员IntArray类型的groupsArray类型slots其中groups用来记录一个group基本信息和其parent group从而可以构建一个groupslots用来存储每个group具体数据对于这两个数组成员具体操作通过Gap Buffer方式进行处理这是一种数据插入过程中移动Gap区域而不是数组元素方式达到数据更新目的Gap区域移动情况,仅仅通过几个指针操作快速访问数据由于Compose整体结构相对稳定Gap移动频率相对因此大部分数组访问达到O(1)时间复杂度架构图看到实际上每个Composition包含两个SlotTable一个用于写入一个用于读取写入后通过同步机制数据同步用于写入的SlotTable读写隔离有助于频繁操作过程中提高性能

GroupSlotTable数据结构,通过内部的parent属性记录其父Group,从而整体上可以构建为一个树形结构。可以简单理解一个Composable(一个自定义或内置Composable函数)对应一个Group不同类型的Composable对应不同Group类型因此一个Compose,实际上就是Group

Recomposer用于触发管理Compose树的组建和重组Recomposer内部有一个while循环当前DecorViewviewTreeLifecycle的ON_CREATE启动随后如果compositon被标记需要重组,则进行重组流程否则挂起等待

Snapshot快照系统用于State版本进行管理Snapshot的思想类似Git版本管理系统即,父分支快照基于自身拉出一个子分支快照,子快照只可以看到到自身快照内的State值的变更,其他分支相同State变更不可见。子快照可以通过apply接口将自身变更合入到父分支快照。换言之,父快照的值对子快照是可见的,子快照对父快照是不可见的,一直到子快照apply()。每次重组都会创建一个快照重组结束后apply快照快照,同时,快照也是ThreadLocal的,即是线程隔离的。这样每次重组访问State值的版本都是相互隔离影响重组完成后merge

RecomposeScopeComposeSlotTable通过group表达,但并非所有的group都是重组group很多类型为了重组group范围进行标记圈定创建重组group创建对应RecomposeScope保存起来state变更导致重组通过当前state对应scope找到重组范围进行重组

ApplierSlotTable存储只是存储了Compose结构信息以及组合过程中涉及rememberstate数据信息而具体绘制LayoutNode负责Applier就是作为ComposeLayoutNode之间桥梁初次组件或者后续重组完成之后通过Applier通知LayoutNode随后LayoutNode根据提供信息发生了变化Composable(保存在changes中)进行绘制

ChangeList:Composition中changes变量对应具体类型ChangeList存储Composable具体变化这些变化会通过Applier最终体现为LayoutNode变更

LayoutNode:Composable在绘制层面结构体Compose最终转化为LayoutNodemeasuredraw都是LayoutNode进行

二、Compose运行流程

Compose运行流程概述初始化、Composition(组合/重组并收集变更信息)、Applier(应用变更信息)、LayoutNode测量绘制

1. 初始化

Compose Runtime中的ComponentActivity提供setContent扩展方法用来ComposeView嵌入Android固有View体系中。ComposeView首先创建AndroidComposeViewAndroidComposeView通过addView加入到Android View树。Compose设计跨平台AndroidComposeView是针对Android平台的一些具体实现Compose绘制结果最终体现在AndroidComposeView上,AndroidComposeViewonMeasureonLayoutdispatchDraw最终传递Compose体系LayoutNode如此构成ComposeAndroid固有View体系结合

ComposeView中,同时也会RecomposerComposition进行初始化Recomposer用来重组管理,Composition代表Compose组合树的容器。

2. Composition(“组合/重组“变更信息)

2.1 首次组合

一步setConent过程调用RecomposercomposeInitial开始Compose树初始组合也就是构建Compose过程

composingRecomposer重要方法每次组合/重组都会调用可视为真正组合/重组起点为了叙述方便“组合”“重组”统一表述重组composing开始重组首先通过Snapshot创建一个快照副本,后续本次重组读写State这个快照范围进行不会影响其他快照。当然,其他快照也不会影响本次重组快照这样每次重组使用数据都是相互隔离

创建快照开始调用Composition进行重组流程每个Composition有一个Composer执行具体重组动作我们所有@Composable修饰的自定义函数称作ComposablesetContentcontent lambda也是一个ComposableComposer通过invokeComposable开始执行content lambda由此开始一系列Composable递归调用构建Compose我们其中一个Composable为例,每个Composable执行过程通过startXXXGroupSlotTable构建一个Group这个Group将被保存SlotTablegroups数组中。Group在不断创建过程中与Composable函数一样保持的对应的嵌入关系,也就是父子关系。后续会将Composable内相应数据(Group本身数据,函数参数、remember、state值等)保存这个Group对应Slots数组

创建Group通过addRecomposeScope创建一个RecomposeScope这个RecomposeScope代表Groupstate变更对应变化范围随后这个scope本身也会作为对应Group的数据通过updateValue存SlotTableslots数据里

以上完成scope通过insertSlots将其保存changeListWriter通过scope可以找到对应Group及其数据所以过程可以理解新建/变更group暂存changeListWriter

通过Composable不断递归调用整个Compose构建完毕实际上就是StlotTableGroup构建完毕并且构建过程group信息(全量)本暂存changeListWriter待进行具体测量、布局和绘制

2.2 重组

Compose特点之一就是响应式编程,数据变化驱动页面变化这个过程称为重组最长重组便是Composable读取state变更导致重组过程如下

Recomposer每次重组时创建Snapshot时,会注册该Snapshot的read/wri

te监听。在重组过程中,如果遇到某个Composable读取了某个State,就会把该State存储到Composable对应的RecomposerScope内,并将该scope与state的对应关系存储在observations中。当对State进行write写入时, Recomposer会通过Snapshot的writeObserver监听到写入动作,并且通知CompositionComposition通过遍历observations取出state对应scope然后加入invalidations缓存,随后也会将本composition作为invalid加入到Recomposer的compositionInvalidations缓存。

另一方面,Recomposer本身通过WindowRecomposer将自身onCreate声明周期绑定onCreate运行runRecomposeAndApplyChanges函数内部有一个while循环,运行频率会和vsync对齐。平时挂起只要compositionInvalidations有了变更就会开始运行其运行过程compositionInvalidations的compostion取出, 调用其recompose函数recompose之前invalidationsscope取出找到对应Group调用Composer的doCompose进行变更

state变更可能导致Composable位置移动删除插入这些转化SlotTable对为Group操作更新SlotTableGroup变更信息保存changeListWriter

上面过程可以看出state变化并不同步导致compose立即更新而是存在

invalidations等待下一vsync信号处理

3. Applier(应用变更信息)

现在无论初始组合还是重组最终变更信息保存changeListWriter我们来看changeListWriter如何最终转变具体页面变更

重组过程每一个Group操作最后转变一条指令通知给changeListWriter比如新建一个Group会调用changeListWriterinsertSlots函数insertSlotsChangeListpush一条指令这条指令保存在ChangeListoptions。RecomposerrunRecomposeAndApplyChanges在后续会调用composition.applyChanges随后取出options指令进行执行

具体执行每种指令具体转化Options一个具体子类例如创建一个Node GroupinsertSlotsInsertNodeFixup执行随后通知作为root的LayoutNode,进行创建group对应的LayoutNode并加入其LayoutNode树的对应位置。

每种Group新建移动删除操作最后变成响应layoutNode新建移动删除操作

4. LayoutNode测量、布局和绘制

LayoutNode结构变更完了随后需要将其绘制出来整个过程主要分为MeasureLayoutDrawMeasureAndLayoutDelegateLayoutNode测量代理执行测量具体方法每次LayoutNode变更会将变更layoutNode存储MeasureAndLayoutDelegaterelayoutNodes然后会通过传统的方式通知AndroidComposeView invalid此时onMeasure会被回调随后onMeasure调用measureAndLayout取出MeasureAndLayoutDelegaterelayoutNodes缓存然后进行下到上重新测量工作。Layout过程类似

测量布局完毕AndroidComposeViewdispatchDraw系统调用此时通过构建更新LayoutNode对应layer将其绘制封装CanvasAndroidCanvas从而完成绘制工作

此处可见绘制过程只对变更LayoutNode进行绘制由此之前重组的差量变更过程有意义

至此Compose初始化、重组到最终绘制流程大致描述完毕

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

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

相关文章

CppCon 2014 学习: C++ Test-driven Development

“Elephant in the Room”这个比喻常用来形容那些大家都知道但没人愿意讨论的重大问题。 这段内容讲的是软件质量管理的经典做法和潜在的问题: 经典做法:开发完成后才进行人工测试(manual testing after creation)。隐喻“Cape o…

RAGflow详解及实战指南

目录 前言 一、RAGflow核心技术解析 1. 技术原理:检索与生成的协同进化 2. 架构设计:分层模块化与高扩展性 3. 核心优势:精准、高效、安全 二、RAGflow实战应用场景 1. 企业知识库搭建 2. 智能客服系统 3. 投资分析报告生成 4. 制造…

[Godot] 如何导出安卓 APK 并在手机上调试

在之前的文章中,我们已经详细介绍了如何配置 Godot 的安卓应用开发环境,包括安装 Android SDK、配置 Java 环境、设置 Godot 的 Android 导出模板等。本篇文章将进一步讲解如何将 Godot 项目导出为安卓 APK 文件,并实现在手机上进行调试运行。…

Linux《文件系统》

在之前的系统IO当中已经了解了“内存”级别的文件操作,了解了文件描述符、重定向、缓冲区等概念,在了解了这些的知识之后还封装出了我们自己的libc库。接下来在本篇当中将会将视角从内存转向磁盘,研究文件在内存当中是如何进行存储的&#xf…

NLP学习路线图(十六):N-gram模型

一、为何需要语言模型?概率视角下的语言本质 自然语言处理的核心挑战在于让机器“理解”人类语言。这种理解的一个关键方面是处理语言的歧义性、创造性和结构性。语言模型(Language Model, LM)为此提供了一种强大的数学框架:它赋…

Python训练第四十天

DAY 40 训练和测试的规范写法 知识点回顾: 彩色和灰度图片测试和训练的规范写法:封装在函数中展平操作:除第一个维度batchsize外全部展平dropout操作:训练阶段随机丢弃神经元,测试阶段eval模式关闭dropout 昨天我们介绍…

InternVL2.5-多模态大模型评估专业图片

具备图像理解功能的大模型InternVL2.5,能有效解析大部分图片。 对于专业图片如医学细胞切片,从专业角度解析,能推动模型应用到更广泛的领域。 InternVL2.5解析示例 prompt(胸部癌变细胞图片,来自PanNuke) 请评估这个组织的风险 InternVL2.…

医疗数理范式化:从范式迁移到认知革命的深度解析

引言 在当代医疗领域,数理思维已经从辅助工具逐渐发展成为核心决策支持系统的关键组成部分。随着数字技术的迅猛发展,医疗行业正经历着前所未有的变革,而数理思维作为这一变革的核心驱动力,正在深刻重塑医疗实践的方方面面。数理思维在医疗领域的应用,本质上是将抽象的数…

图神经网络在信息检索重排序中的应用:原理、架构与Python代码解析

现代信息检索系统和搜索引擎普遍采用两阶段检索架构,在人工智能应用中也被称为检索增强生成(Retrieval-Augmented Generation, RAG)。在初始检索阶段,系统采用高效的检索方法,包括词汇检索算法(如BM25&…

现代数据湖架构全景解析:存储、表格式、计算引擎与元数据服务的协同生态

本文全面剖析现代数据湖架构的核心组件,深入探讨对象存储(OSS/S3)、表格式(Iceberg/Hudi/Delta Lake)、计算引擎(Spark/Flink/Presto)及元数据服务(HMS/Amoro)的协作关系,并提供企业级选型指南。 一、数据湖架构演进与核心价值 数据湖架构演进历程 现代数据湖核心价…

全志F1c200开发笔记——移植Debian文件系统

1.搭建环境 sudo apt install qemu-user-static -y sudo apt install debootstrap -y mkdir rootfs 2.拉取文件系统 这边我参照墨云大神的文档,但是华为镜像已经没有armel了,我找到了官方仓库,还是有的,拉取速度比较慢 sudo d…

支持功能安全ASIL-B的矩阵管理芯片IS32LT3365,助力ADB大灯系统轻松实现功能安全等级

随着自动驾驶技术的快速发展,汽车前灯智能化也越来越高。自适应远光灯 (ADB) 作为一种智能照明系统,在提升驾驶安全性和舒适性方面发挥着重要作用。ADB 系统通过摄像头和传感器获取前方道路信息,例如来车的位置、距离和速度,并根据…

BFS入门刷题

目录 P1746 离开中山路 P1443 马的遍历 P1747 好奇怪的游戏 P2385 [USACO07FEB] Bronze Lilypad Pond B P1746 离开中山路 #include <iostream> #include <queue> #include <cstring> using namespace std; int n; int startx, starty; int endx, endy; …

UE5 编辑器工具蓝图

文章目录 简述使用方法样例自动生成Actor&#xff0c;并根据模型的包围盒设置Actor的大小批量修改场景中Actor的属性&#xff0c;设置Actor的名字&#xff0c;设置Actor到指定的文件夹 简述 使用编辑器工具好处是可以在非运行时可以对资源或场景做一些操作&#xff0c;例如自动…

数据仓库分层 4 层模型是什么?

企业每天都在产生和收集海量数据。然而&#xff0c;面对这些数据&#xff0c;许多企业却陷入了困境&#xff1a;如何高效管理、处理和分析这些数据&#xff1f;如何从数据中提取有价值的信息来支持业务决策&#xff1f;这些问题困扰着众多数据分析师和 IT 管理者。 在众多架构…

基于亚博K210开发板——物体分类测试

开发板 亚博K210开发板 实验目的 本次测试主要学习 K210 如何物体分类&#xff0c;然后通过 LCD 显示屏实时显示当前物体的分类名称。本节采用百度出的 PaddlePaddle 平台开发。 实验元件 OV2640 摄像头/OV9655 摄像头/GC2145 摄像头、LCD 显示屏 硬件连接 K210 开发板…

什么是缺页中断(缺页中断详解)

文章目录 【操作系统】什么是缺页中断&#xff08;缺页中断详解&#xff09;一、缺页中断的本质与背景1. **虚拟内存与分页机制**2. **缺页中断的定义** 二、缺页中断的触发场景1. **首次访问新分配的虚拟页**2. **内存置换导致的页缺失**3. **访问权限冲突**4. **页表项无效**…

【echarts】仪表盘

<div style"width:50%;height:33%"><Yibiaopan echart_id"ybpChart2" :series_data"gaugeData2" title"火电" unit"MWh" :colorList"[#DFA58F,#F89061,#FF8E59]" /></div> 链接&#xff1a;ht…

java27

1.IO流 FileOutPutStream字节输出流基本用法&#xff1a; 一次性写入一个字符串的内容&#xff1a; 注意&#xff1a;\r或者\n表示把普通的r或者n的字符转义成回车的意思&#xff0c;所以不需要\\ FileInputStream字节输入流基本用法 -1在ASCII码里面对应的符号&#xff1a; 不…

OpenFeign和Gateway集成Sentinel实现服务降级

目录 OpenFeign集成Sentinel实现fallback服务降级cloud-alibaba-payment8003(支付服务)cloud-common-api(通用模块)cloud-alibaba-order9003(订单服务)Sentinel配置流控规则测试结果 Gateway集成Sentinel实现服务降级cloud-gateway9527(网关)测试结果 总结 OpenFeign集成Sentin…