从 Jetpack Compose 迁移
ViewCompose 受到 Compose 启发,但不是 Compose 兼容层。成功迁移的目标是保留所有权、 生命周期和可观察行为,而不是替换名称相似的函数。在把页面迁移到原生 Android View 渲染器之前,先用本节识别语义缺口。
最后验证日期:2026-08-22
复核责任人:Kernel、UI Foundation、Android Engine、Android 聚合层与 navigation 模块族的维护者
已验证的源状态与目标状态
迁移目标是下面这组独立版本化的 ViewCompose 模块:
| 模块族 | 产物 | 已验证版本 |
|---|---|---|
| 状态与组合 | viewcompose-runtime、viewcompose-ui-foundation | runtime 0.1.0-alpha02;UI Foundation 0.1.0-alpha01 |
| UI 与渲染 | viewcompose-ui-contract、viewcompose-renderer-android、viewcompose-constraintlayout-androidx | contract 0.1.0-alpha03;renderer/ConstraintLayout 0.1.0-alpha01 |
| Android 所有权 | viewcompose-android、viewcompose-material3-android、viewcompose-host-android、viewcompose-lifecycle-androidx、viewcompose-viewmodel-androidx | 聚合层/集成层 0.1.0-alpha01;host 0.1.0-alpha03 |
| 导航 | viewcompose-navigation-core、viewcompose-navigation-android | core 0.1.0-alpha02;Android 0.1.0-alpha01 |
| 动画 | viewcompose-animation-core、viewcompose-animation | 均为 0.1.0-alpha04 |
不可变的发布源码 revision 记录在
gradle/viewcompose-publishing.properties。
上游语义基线如下:
| 依赖族 | 版本 |
|---|---|
| Compose Runtime、UI 和 Foundation | 1.11.4 |
| Activity | 1.13.0 |
| Lifecycle | 2.11.0 |
| SavedState | 1.5.0 |
| Navigation 2 | 2.9.8 |
| Navigation 3 | 1.1.4 |
仓库内可执行对比基线仍为 Compose 1.7.8、Activity 1.12.4、Lifecycle 2.8.7 和
Kotlin 2.0.21,声明位置是
gradle/libs.versions.toml。
较新的上游语义由 Android 官方文档和发布说明确定;ViewCompose 行为则由本地源码、测试和
可编译样例确定。通过基于 1.7.8 的本地对比,不能证明与 1.11.4 语义等价。
本文不声明性能等价。未来任何性能对比都必须说明设备、构建模式、工作负载、预热、采样和 统计方法。
选择迁移路径
| 源代码关注点 | 从这里开始 | 实现前必须确定 |
|---|---|---|
| 状态、重组、key、Effect 或可保存状态 | 状态、重组与恢复 | 状态所有者、重启边界、身份、Effect 提交点和恢复生命周期 |
| 布局、Modifier、density、local、inset 或 Android View 输出 | 布局、Modifier 与环境 | 测量引擎、Modifier 折叠、逻辑边、local 失效和 inset 所有者 |
| Activity、Fragment、现有 View 宿主、生命周期、ViewModel 或 Android 互操作 | 宿主、生命周期与 Android 互操作 | 根所有者、销毁边界、已安装 owner、可重放工作和释放清理 |
| Navigation 2 或 Navigation 3 | 导航 | 源导航模型、路由身份、owner 作用域、隐藏 session 策略和 Back 集成 |
| 图片加载 | 图片加载 | source 类型、loader 所有权、request 策略和回收 View 释放 |
| Lazy 集合与 Pager | Lazy 集合 Revision 与复用 | 语义 Revision、Mounted Tree 复用、互操作 Reset/Release,以及 TabRow/Pager 硬切 |
| 组件 DSL 别名、交互反馈、TextField Wrapper 或仅 Alpha 的内容动画 | DSL 契约收敛 | Variant 替代、Indication 所有权、类型化输入 Profile 与 Crossfade 命名 |
物理动画、Animatable、内容/可见性过渡、Seek、Bounds、共享运动或动画工具 | 动画 | 时长与物理语义、速度、子树身份、几何所有者和检查激活条件 |
一个边界跨越多个关注点时,需要阅读多份页面。例如,导航目的地中的
rememberSaveable 同时受状态/恢复契约和导航所有权契约约束。
统一能力矩阵
下面的矩阵用于做粗粒度迁移决策。具体契约和证据以链接页面为准。所有页面对状态术语使用 同一含义:
- Supported(支持)——存在迁移所需的行为,并有仓库证据支撑。
- Partially supported(部分支持)——主要用例存在,但重要 API 或语义边界更窄或不同。
- Intentionally different(刻意不同)——ViewCompose 有意采用另一种所有权或执行模型, 代码必须重新设计。
- Unsupported(不支持)——当前版本没有对应的公开能力。
| 领域 | 能力 | 状态 | 迁移决策 | 详情 |
|---|---|---|---|---|
| 状态 | 可变状态、变更策略和读取观察 | Supported(支持) | 保留状态所有权;不要依赖 Compose 的回调次数或线程。 | 状态 |
| 状态 | 派生状态和快照事务 | Partially supported(部分支持) | 检查相等结果抑制、嵌套、冲突和线程规则。 | 状态 |
| 状态 | 快照集合和 snapshotFlow | Partially supported(部分支持) | 已提供 snapshotFlow;快照集合仍需使用 MutableState 中的不可变值。 | 状态 |
| 组合 | 编译器生成的重启、稳定性和 strong skipping | Intentionally different(刻意不同) | 选择显式 ViewCompose group,并把读取放到最小更新边界。 | 重组 |
| 组合 | 位置 remember 和 keyed identity | Partially supported(部分支持) | 保持调用顺序稳定;不要依赖普通 keyed 兄弟节点在重排时移动。 | 身份 |
| Effect | SideEffect、DisposableEffect、LaunchedEffect 和 produceState | Supported(支持) | 把外部工作移到已提交 Effect,并显式处理失败清理。 | Effect |
| 恢复 | rememberSaveable、Saver 和宿主恢复 | Partially supported(部分支持) | 优先使用自动 key、保持值精简,并为自定义宿主显式安装服务。 | 恢复 |
| 布局 | 内置容器、尺寸、fill 和 parent data | Partially supported(部分支持) | 按 Android View 测量和 LayoutParams 重新验证行为。 | 布局 |
| 布局 | 通用自定义测量 | Unsupported(不支持) | 使用内置容器、ConstraintLayout 或由生命周期所有的 Android ViewGroup。 | 自定义测量 |
| Modifier | padding、margin、顺序和渲染器折叠 | Intentionally different(刻意不同) | 规范化链,并应用各 Modifier 家族的既定解析规则。 | Modifier 折叠 |
| Modifier | 结构相等性和渲染器复用 | Supported(支持) | 使用具有语义的稳定 key;新的回调对象不一定是更新信号。 | Modifier 相等性 |
| Modifier | 应用自定义 Modifier.Node 生命周期 | Unsupported(不支持) | 使用受支持 Modifier、互操作或经过审查的 UI-contract 与 renderer 能力。 | Modifier.Node |
| 环境 | density 和 font scale | Supported(支持) | 保留逻辑 dp/sp 值,只在渲染器边界转换。 | 环境 |
| 环境 | locale、布局方向以及逻辑/物理边 | Supported(支持) | start/end 意图使用相对 API,明确 left/right 行为才使用物理 API,并测试 RTL 输出。 | 环境 |
| 环境 | 用 UiLocal 替代 CompositionLocal | Intentionally different(刻意不同) | 用可观察状态支撑变化的 local;只读取 local 不会让读取者失效。 | UiLocal |
| Insets | 系统栏、IME 和嵌套消费 | Partially supported(部分支持) | 每条边指定一个所有者,并验证 View/ViewCompose 混合处理。 | Insets |
| 互操作 | ViewCompose AndroidView 回调生命周期 | Intentionally different(刻意不同) | 分离可重放 update/reset、事务后 commit 和永久 release 清理。 | Android View 互操作 |
| 宿主 | Activity 和 Fragment 根 | Partially supported(部分支持) | 考虑内部所有的 session,以及 Fragment owner 与销毁时机不一致。 | 标准宿主 |
| 宿主 | 现有容器中的 renderInto | Partially supported(部分支持) | 安装所有必需 owner,并显式销毁返回的 session。 | 自定义宿主 |
| 所有权 | 通用 UI 作用域 ViewModel 和继承的 CreationExtras | Partially supported(部分支持) | 验证目的地/图的 factory 输入;当前没有任意子树 provider。 | Owner |
| Session | 显式渲染、帧调度和终结性销毁 | Intentionally different(刻意不同) | 把 RenderSession 视为组合、原生树、overlay 和清理的所有者。 | Session |
| 互操作 | 直接 ViewBinding 和树内 Fragment API | Unsupported(不支持) | 把 Fragment 所有权留在渲染树外,并显式管理 XML inflate。 | 不支持的互操作 |
| 导航 | controller、目的地和多栈所有权 | Intentionally different(刻意不同) | 迁移目标状态转换,不要迁移 Navigation 2 或 3 的 API 名称。 | 导航模型 |
| 导航 | 图、类型化路由和栈操作 | Partially supported(部分支持) | 使用受支持的基础 NavValue 参数和单个事务命令。 | 路由与事务 |
| 导航 | entry/graph owner 和 Lifecycle 2.11 factory 继承 | Supported(支持) | 保留继承的父级 Factory/extra,并隔离重复 route 的栈 owner。 | Entry 所有权 |
| 导航 | 目的地生命周期和自适应 pane | Intentionally different(刻意不同) | 允许多个 resumed entry;不要从 RESUMED 推断唯一可见性。 | 生命周期 |
| 导航 | 隐藏目的地保留组合 | Partially supported(部分支持) | 让后台工作感知生命周期;隐藏 session 会保留 Effect 和原生 View。 | 保留 |
| 导航 | 深链 | Partially supported(部分支持) | 替换 action/MIME 规则;未声明 query 值可存在,但不能影响导航策略。 | 深链 |
| 导航 | 保存/恢复、系统 Back 和 Predictive Back | Supported(支持) | 恢复后重建存活对象,并在发布流程中保留设备验证。 | 恢复与 Back |
| 导航 | 直接 NavigationEvent 集成 | Unsupported(不支持) | 把 dispatcher-owner、forward event、测试 fake 和 Preview 需求留在 ViewCompose 外。 | NavigationEvent |
| 动画 | 时长采样、target-as-state、自主 Transition、淡入淡出/尺寸可见性、Crossfade 与内容尺寸动画 | Partially supported(部分支持) | 只使用当前已记录子集,不要把带时长的 SpringSpec 当成物理弹簧。 | 动画 |
| 动画 | 物理弹簧、Decay、Seekable Transition、Bounds、共享运动与时间线检查 | Unsupported(不支持) | 遵循已接受的分阶段契约;计划 API 发布前不是迁移目标。 | 动画 |
迁移顺序
- 记录源 Compose、Activity、Lifecycle、SavedState 和 Navigation 版本。
- 修改 UI 声明前,盘点状态、生命周期、ViewModel、导航和持久数据的所有者。
- 标记编译器重启边界、布局测量假设、Modifier 顺序、逻辑边、local 和 inset 所有权。
- 用上面的矩阵分类每项必需能力。实现前先停止并重新设计所有不支持的依赖。
- 每次迁移一个可独立测试的页面或子树。不要在没有独立行为断言时,同时重写宿主、导航 模型和持久化。
- 编译目标代码,并按页面需求验证重组、配置重建、进程重建、RTL、inset、Android View 回滚、Back 和生命周期行为。
- 列出的任一上游或 ViewCompose 版本变化后,重新执行对比。
可执行迁移基准
文档片段不能成为第二份事实来源。请使用仓库中这些可编译样例:
:samples:compose-migration模块包含四篇详细迁移文档嵌入的状态、布局/环境、宿主/Android 互操作和 Navigation 2 成对片段;- 计数器应用 组合了 Activity 宿主、remember 可变状态、View 布局、Modifier 和输入;
- runtime 样例 覆盖可变与派生状态、快照事务、策略、观察和组合;
- UI Foundation 样例 覆盖可保存状态 registry 和主题所有权;
- Android 宿主样例 覆盖 Activity、Fragment、自定义容器和 Android View 宿主;
- navigation-core 样例 覆盖图、深链、事务和生命周期规划;
- Android 导航样例 覆盖 remember 宿主、controller 操作和 motion 配置。
根 qaQuick 任务会编译这些样例源码集或使用它们的测试。它还会运行
verifyMigrationPairedSamples,拒绝中英文页面中缺失、额外、乱序或过期的成对片段。仅设备
可验证的恢复和 Predictive Back 证据仍由状态与恢复对照
及英文导航指南链接的流程治理。
已知契约缺口
在源码文档、实现和可执行证据就以下问题达成一致前,不要提高能力状态:
- 相等结果与嵌套派生状态以及只读快照嵌套需要专项回归覆盖。
- 重复 size/padding、嵌套 inset 消费和 native-view 回调身份需要更广的可执行覆盖。
- Lifecycle
2.11.0任意 UI 作用域没有 ViewCompose 等价证据。 - 最新 Predictive Back 设备运行仍比完整语义基线更窄。
复核必须先检查上游官方文档,再检查不可变 ViewCompose 源码契约、测试、可编译样例和适用的 设备流程。签名匹配或 API 名称相似永远不足以证明语义等价。