A2UIRender 架构重构设计文档
1. 设计背景
1.1 当前问题
- 调用顺序依赖问题:当前实现假设
bindToRender先于onReceive调用,但开发者可能先new SurfaceController并通过onReceive传输 DSL,然后再与A2UIRenderComponent绑定 - 强耦合设计:
SurfaceController.onReceive()直接依赖currentRootSlot,但这个值在onReceive时可能为undefined - 单一 Surface 限制:当前架构无法支持一个 RenderContext 内部管理多个 Surface 的场景
1.2 设计目标
- 解耦 DSL 解析与 UI 绑定:
onReceive和bindToRender应该可以任意顺序调用 - 支持多 Surface 管理:通过 RenderSlot 管理内部多个 Surface,使用栈结构
- 统一管理:通过 RenderManager 统一管理所有 RenderSlot
2. 架构设计
2.1 核心概念
2.1.1 RenderId(渲染 ID)
- 作用:唯一标识一个 SurfaceController 实例
- 生成方式:通过 ETS 侧的 ID 生成器自增生成
- 生命周期:随 SurfaceController 创建而创建,随 SurfaceController 销毁而失效
2.1.2 RenderSlot(渲染槽)
- 作用:管理一个 SurfaceController 内部的多个 Surface(Surface即SurfaceSlot)
- 结构:使用surefaceManager 管理多个 Surface
- 成员:
renderId: 所属的渲染 IDsurfaceManager: Surface 管理器,用于创建和查询 Surface
2.1.3 RenderManager(渲染管理器)
- 作用:管理进程内所有的 RenderSlot
- 实现:C++ 单例类
- 职责:
- 创建和销毁 RenderSlot
- 通过 renderId 查找 RenderSlot
- 维护 renderId 到 RenderSlot 的映射
2.2 类设计
2.2.1 ETS 侧
┌─────────────────────────────────────────┐
│ RenderIdGenerator │
├─────────────────────────────────────────┤
│ - nextId: number │
│ + generateId(): number │
└─────────────────────────────────────────┘
│
│ uses
▼
┌──────────────────────────────────────────────────────────┐
│ SurfaceController │
├──────────────────────────────────────────────────────────┤
│ - renderId: number │
│ - catalog: Catalog │
│ - surfaceBelongsTo?: string │
│ - onAction?: ActionReceiver │
│ - onError?: ErrorCallback │
│ - onSurfaceEvent: SurfaceEventCallback │
│ - currentRootSlot?: NodeContent │
│ + constructor(catalog, eventCallback, checkSchema) │
│ + onReceive(dsl: string): void │
│ + bindToRender(rootSlot: NodeContent): void │
└──────────────────────────────────────────────────────────┘
2.2.2 C++ 侧
┌─────────────────────────────────────────┐
│ RenderManager │
├─────────────────────────────────────────┤
│ - renderSlots_: Map<renderId, RenderSlot>│
│ + GetInstance(): RenderManager& │
│ + CreateRenderSlot(renderId): RenderSlot&│
│ + FindRenderSlot(renderId): RenderSlot* │
│ + RemoveRenderSlot(renderId): void │
└─────────────────────────────────────────┘
│
│ manages
▼
┌──────────────────────────────────────────────────────────┐
│ RenderSlot │
├──────────────────────────────────────────────────────────┤
│ - renderId_: number │
│ - surfaceManager_: SurfaceManager │
│ + RenderSlot(renderId: number) │
│ + GetRenderId(): number │
│ + PushSurface(surfaceId: string): SurfaceSlot& │
│ + PopSurface(): void │
│ + GetTopSurface(): SurfaceSlot* │
│ + FindSurface(surfaceId: string): SurfaceSlot* │
│ + BindTopSurfaceToRoot(contentHandle): void │
└──────────────────────────────────────────────────────────┘
│
│ manages
▼
┌─────────────────────────────────────────┐
│ SurfaceSlot (existing) │
├─────────────────────────────────────────┤
│ - surfaceId_: string │
│ - contentHandle_: ArkUI_NodeContentHandle│
│ - rootComponent_: Component │
│ - bindingEngine_: BindingEngine │
│ + SetSurfaceId(surfaceId) │
│ + GetSurfaceId(): string │
│ + SetContentHandle(handle) │
│ + UpdateComponents(messageBody) │
│ + UpdateDataModel(messageBody) │
└─────────────────────────────────────────┘
2.3 时序图
2.3.1 SurfaceController 构造和初始化
SurfaceController RenderIdGenerator C++ Layer
│ │ │
│ constructor() │ │
│───────────────────────────>│ │
│ │ │
│ generateId() │ │
│<───────────────────────────│ │
│ │ │
│ initRenderSlot(renderId) │ │
│───────────────────────────────────────────────────>│
│ │ │
│ │ │ CreateRenderSlot(renderId)
│ │ │──────────────>
│ │ │
│ │ │ new RenderSlot(renderId)
│ │ │──────>
│ │ │
│ │ │ new SurfaceManager()
│ │ │──────>
2.3.2 onReceive 调用(先于 bindToRender)
SurfaceController A2UIMessage RenderManager RenderSlot
│ │ │ │
│ onReceive(dsl) │ │ │
│─────────────────────────>│ │ │
│ │ │ │
│ fromDSL(dsl) │ │ │
│<──────────────────────────│ │ │
│ │ │ │
│ processMessage(renderId, a2uiMessage, catalog) │ │
│──────────────────────────────────────────────────────────────────────>│
│ │ │ │
│ │ │ FindRenderSlot(renderId)
│ │ │<──────────────────│
│ │ │ │
│ │ │ GetSurface(surfaceId)
│ │ │<──────────────────│
│ │ │ │
│ │ │ UpdateComponents() / UpdateDataModel()
│ │ │───────────────────>│
2.3.3 bindToRender 调用
SurfaceController RenderManager RenderSlot SurfaceSlot
│ │ │ │
│ bindToRender(rootSlot) │ │ │
│─────────────────────────────────────────────────────────────────────────>│
│ │ │ │
│ bindSurfaceToRender(renderId, contentHandle)│ │
│─────────────────────────────────────────────>│ │
│ │ │ │
│ │ FindRenderSlot(renderId) │
│ │<──────────────────│ │
│ │ │ │
│ │ GetTopSurface() │ │
│ │<──────────────────│ │
│ │ │ │
│ │ │ SetContentHandle()│
│ │ │───────────────────>│
2.3.4 onReceive 后调用 bindToRender
SurfaceController RenderManager RenderSlot SurfaceSlot
│ │ │ │
│ onReceive(dsl) │ │ │
│─────────────────────────────────────────────>│ │
│ │ │ │
│ │ CREATE_SURFACE │ │
│ │ PushSurface() │ │
│ │ │───────────────────>│
│ │ │ │
│ bindToRender(rootSlot) │ │ │
│─────────────────────────────────────────────>│ │
│ │ │ │
│ │ GetTopSurface() │ │
│ │ │───────────────────>│
│ │ │ SetContentHandle()│
3. 实现计划
3.1 ETS 侧实现
-
创建 RenderIdGenerator 类
- 实现单例模式
- 提供自增 ID 生成方法
-
修改 SurfaceController 类
- 添加
renderId成员变量 - 构造函数中调用 ID 生成器
- 添加
initRenderSlot()方法调用 NAPI 接口 - 修改
onReceive()方法,传递 renderId 而不是 currentRootSlot - 修改
bindToRender()方法,通过 renderId 绑定
- 添加
-
更新 NAPI 接口
- 添加
initRenderSlot(renderId)接口 - 修改
processMessage(renderId, message, catalog)接口 - 添加
bindSurfaceToRender(renderId, contentHandle)接口
- 添加
3.2 C++ 侧实现
-
创建 RenderManager 类
- 实现单例模式
- 管理 renderId 到 RenderSlot 的映射
- 提供创建、查找、删除 RenderSlot 的方法
-
创建 RenderSlot 类
- 保存 renderId
- 包含 Surface 栈(std::stack)
- 包含 SurfaceManager 实例
- 提供 PushSurface、PopSurface、GetTopSurface 等方法
- 提供 BindTopSurfaceToRoot 方法
-
修改 SurfaceManager(可选)
- 保留现有的 SurfaceManager
- 或者将其功能整合到 RenderSlot 内部
-
更新 NAPI 绑定
- 添加
InitRenderSlot方法 - 修改
HandleIncomingMessage为ProcessMessage - 添加
BindSurfaceToRender方法
- 添加
3.3 测试策略
-
单元测试
- 测试 RenderIdGenerator 的自增功能
- 测试 SurfaceController 的初始化
- 测试 RenderSlot 的栈操作
-
集成测试
- 测试先 onReceive 后 bindToRender 的流程
- 测试先 bindToRender 后 onReceive 的流程
- 测试多个 Surface 的创建和管理
-
回归测试
- 确保现有的测试用例仍然通过
- 确保现有的功能不受影响
4. 风险与注意事项
- 向后兼容性:需要确保现有的代码仍然能够正常工作
- 性能影响:RenderSlot 的查找操作不应该影响性能
- 内存管理:确保 RenderSlot 和 SurfaceSlot 的正确销毁
- 线程安全:RenderManager 需要考虑线程安全问题(如果有多线程场景)
5. 实施步骤
- 第一阶段:实现 RenderIdGenerator 和修改 SurfaceController(ETS)
- 第二阶段:实现 RenderManager 和 RenderSlot(C++)
- 第三阶段:更新 NAPI 绑定
- 第四阶段:测试和验证