概述
UINode 是 ACE Engine (ArkUI) 中组件树的基础节点类。它定义了组件树的基本结构(父子关系)和生命周期管理,但不包含布局(Layout)和渲染(Render)逻辑(这些由子类 FrameNode 负责)
- 源码位置: sources/arkui_ace_engine/frameworks/core/components_ng/base/ui_node.h`
- 继承关系:
AceType->UINode - 核心职责:
- 树结构管理**:维护父子节点关系 (
AddChild,RemoveChild,parent_,children_)。 - 生命周期:挂载/卸载 (
AttachToMainTree,DetachFromMainTree)。 - 上下文关联:持有
PipelineContext,连接到渲染管线。
- 树结构管理**:维护父子节点关系 (
类图
classDiagram
class AceType {
<<abstract>>
+GetTypeName()
+InstanceOf()
+DynamicCast()
}
class UINode {
<<核心基类>>
#children_: list~RefPtr~UINode~~
#parent_: WeakPtr~UINode~
#context_: PipelineContext*
#nodeId_: int32_t
#tag_: string
+AddChild(child, slot)
+RemoveChild(child)
+MountToParent(parent, slot)
+GetParent() RefPtr~UINode~
+GetChildren() list~RefPtr~UINode~~
+TouchTest(...) HitTestResult
+MouseTest(...) HitTestResult
+AxisTest(...) HitTestResult
+MarkDirtyNode(flag)
+UpdateLayoutPropertyFlag()
+CreateLayoutWrapper() RefPtr~LayoutWrapperNode~
+AttachContext(context)
+DetachContext()
+OnAttachToMainTree()
+OnDetachFromMainTree()
+GetContext() PipelineContext*
+GetId() int32_t
+GetTag() string
}
class LayoutWrapper {
<<布局包装器>>
+Measure()
+Layout()
}
class FrameNode {
<<实际渲染节点>>
+pattern_: RefPtr~Pattern~
+layoutProperty_: RefPtr~LayoutProperty~
+renderContext_: RefPtr~RenderContext~
+eventHub_: RefPtr~EventHub~
+GetPattern() RefPtr~Pattern~
+GetLayoutProperty() RefPtr~LayoutProperty~
+GetRenderContext() RefPtr~RenderContext~
}
class GroupNode {
<<组合节点>>
+AddChildToGroup(child, slot)
+DeleteChildFromGroup(slot)
}
class ForEachNode {
<<语法节点:循环>>
+CompareAndUpdateChildren()
+FlushUpdateAndMarkDirty()
}
class IfElseNode {
<<语法节点:条件>>
+UpdateCondition()
}
class WithThemeNode {
<<语法节点:主题>>
+ApplyTheme()
}
class PipelineContext {
<<渲染上下文>>
+GetRootNode() RefPtr~UINode~
+FlushPipeline()
}
class LayoutWrapperNode {
<<布局包装节点>>
+Measure()
+Layout()
}
class FocusHub {
<<焦点管理>>
+RequestFocus()
+LostFocus()
}
AceType <|-- UINode : virtual继承
UINode <|-- FrameNode : 继承
UINode <|-- ForEachNode : 继承
UINode <|-- IfElseNode : 继承
UINode <|-- WithThemeNode : 继承
FrameNode <|-- GroupNode : 继承
FrameNode --|> LayoutWrapper : 多重继承
UINode --> PipelineContext : 关联
UINode --> LayoutWrapperNode : 创建
FrameNode --> FocusHub : 包含
UINode "1" *-- "0..*" UINode : children
UINode "0..1" <-- "1" UINode : parent
- 层次类型: 第一层:基础类型层
AceType:类型系统基类,提供 RTTI 和智能指针支持
第二层:节点基类层
UINode:节点树管理、事件分发、生命周期
第三层:具体节点层
FrameNode:实际渲染节点,包含 Pattern、LayoutProperty、RenderContextSyntaxNode系列:语法控制节点(ForEach、IfElse、WithTheme 等)
第四层:特殊节点层
GroupNode:组合节点,用于特殊布局场景
关键设计模式
- 组合模式:
UINode通过children_管理子节点,形成树结构 - 模板方法:
UINode定义骨架,子类实现具体行为(如IsAtomicNode()) - 观察者模式:通过
PipelineContext管理节点生命周期 - 策略模式:
Pattern决定FrameNode的行为
3. 核心成员变量
| 变量名 | 类型 | 作用 |
|---|---|---|
children_ | std::list<RefPtr<UINode>> | 存储子节点列表。使用链表是为了高效插入和删除。 |
parent_ | WeakPtr<UINode> | 指向父节点。使用WeakPtr 防止循环引用(父->子,子->父)。 |
context_ | PipelineContext* | 指向管线上下文,用于访问全局服务(如主题、任务队列)。 |
tag_ | std::string | 组件标签(如 "Column", "Button")。 |
nodeId_ | int32_t | 全局唯一的节点 ID。 |
onMainTree_ | bool | 标记节点 |
4. 核心机制
4.1 树操作
这里我们可以把UINode理解为前端的DOM节点, 提供了相应的API:
AddChild(child): 添加子节点。RemoveChild(child): 移除子节点。MountToParent(parent): 将自己挂载到指定父节点。
4.2 语法节点 & 渲染节点
类似前端操作的visibility操作, 很多时候我们需要依据逻辑来控制组件是否显示。 ohos提供了相应的
if/else语义能力(同时, foreach、lazyForeach )。 为了从底层支持这个能力, UINode 被拆分为两种:
- FrameNode:实体节点,有布局和渲染能力(如
Column,Text)。 - SyntaxNode(及其他非 FrameNode 子类):语法节点,无渲染实体,仅用于控制流(如
IfElseNode,ForEachNode)。
4.3 渲染流程
PipelineContext (渲染上下文)
↓
UINode Tree (节点树)
├── FrameNode (实际渲染节点)
│ ├── Pattern (行为策略)
│ ├── LayoutProperty (布局属性)
│ └── RenderContext (渲染上下文)
└── SyntaxNode (语法控制节点)
└── 管理子 FrameNode
↓
LayoutWrapper (布局计算)
↓
RenderNode (渲染节点)
5. 核心函数
5.1 节点创建
UINode::UINode(const std::string& tag, int32_t nodeId, bool isRoot)
: tag_(tag), nodeId_(nodeId), accessibilityId_(currentAccessibilityId_++), isRoot_(isRoot)
{
++count_; // 全局 用来统计当前存活的UINode 数量, 我也不知为什么统计
if (MultiThreadBuildManager::IsThreadSafeNodeScope()) {// 判断线程安全, 这里1.2 会进来, 增量引擎会存在并行化场景
isThreadSafeNode_ = true;
SetIsFree(true);
}
if (AceChecker::IsPerformanceCheckEnabled()) {// 存放js 代码位置信息, 用来调试补充。 这里没有考虑cangjie和其他语言
auto pos = EngineHelper::GetPositionOnJsCode();
nodeInfo_ = std::make_unique<PerformanceCheckNode>();
nodeInfo_->codeRow = std::get<1>(pos);
nodeInfo_->codeCol = std::get<2>(pos);
nodeInfo_->pagePath = std::get<0>(pos);
}
apiVersion_ = Container::GetCurrentApiTargetVersion();
#ifdef UICAST_COMPONENT_SUPPORTED
do {
auto container = Container::Current();
CHECK_NULL_BREAK(container);
auto distributedUI = container->GetDistributedUI();
CHECK_NULL_BREAK(distributedUI);
distributedUI->AddNewNode(nodeId_);
} while (false);
#endif
instanceId_ = Container::CurrentId();
nodeStatus_ = ViewStackProcessor::GetInstance()->IsBuilderNode() ? NodeStatus::BUILDER_NODE_OFF_MAINTREE
: NodeStatus::NORMAL_NODE;
if (SystemProperties::ConfigChangePerform()) {
auto currentContainer = Container::GetContainer(instanceId_);
isDarkMode_ = currentContainer ? (currentContainer->GetColorMode() == ColorMode::DARK) : false;
}
uiNodeGcEnable_ = FeatureParam::IsUINodeGcEnabled();
}
5.2 节点销毁
UINode::~UINode()
{
--count_;
#ifdef UICAST_COMPONENT_SUPPORTED
do {
auto container = Container::Current();
CHECK_NULL_BREAK(container);
auto distributedUI = container->GetDistributedUI();
CHECK_NULL_BREAK(distributedUI);
if (hostPageId_ == distributedUI->GetCurrentPageId()) {
distributedUI->AddDeletedNode(nodeId_);
}
} while (false);
#endif
if (!removeSilently_) {
ElementRegister::GetInstance()->RemoveItem(nodeId_); //RemoveItem(nodeId_):从 map 里删掉,并且把该 nodeId_ 加入 removedItems_(“已删除 id”集合)
} else {
ElementRegister::GetInstance()->RemoveItemSilently(nodeId_); // 只从 map 里删,不加入 removedItems_。用于“这个节点马上会用同一个或新 id 再注册”的场景,避免被误当成“已删除”。
}
if (isThreadSafeNode_) {
ElementRegisterMultiThread::GetInstance()->RemoveThreadSafeNode(nodeId_);
}
if (propInspectorId_.has_value()) {
ElementRegister::GetInstance()->RemoveFrameNodeByInspectorId(propInspectorId_.value_or(""), nodeId_);
}
if (!onMainTree_) {
return;
}
if (context_) {
context_->RemoveAttachedNode(this); //AttachToMainTree 时会把节点放进 attachedNodeSet_;析构时必须从该集合移除,否则管线会持有已销毁节点的指针,造成悬空引用/未定义行为。
}
onMainTree_ = false;
if (nodeStatus_ == NodeStatus::BUILDER_NODE_ON_MAINTREE) {
nodeStatus_ = NodeStatus::BUILDER_NODE_OFF_MAINTREE;
}
}
6. 其他关键功能
6.1 线程安全节点机制(Thread-Safe Node)
支持非 UI 线程操作节点,主要用于后台构建场景。
核心方法
| 方法 | 功能说明 |
|---|---|
IsThreadSafeNode() | 检查是否为线程安全节点 |
IsFree() | 检查节点是否处于"自由"状态 |
SetIsFree(bool isFree) | 设置节点自由状态 |
PostAfterAttachMainTreeTask() | 提交挂载后的任务 |
ExecuteAfterAttachMainTreeTasks() | 执行挂载后的任务 |
关键状态
isThreadSafeNode_:标识节点是否支持线程安全操作isFree_:节点处于"自由"状态时,非 UI 线程可以安全操作afterAttachMainTreeTasks_:挂载到主树后需要执行的任务列表
使用场景
// 典型场景:LazyForEach 在后台线程构建节点
if (node->IsFree()) {
// 非 UI 线程可以安全操作
node->AddChild(child);
// ...
node->PostAfterAttachMainTreeTask([=]() {
// 挂载到主树后执行的任务
});
}
补充: 并行化的时候需要关注, 要修改节点状态,不然没法进入渲染流程。
6.2 BuilderNode 机制(延迟构建)
支持延迟构建和动态更新,用于 @Builder 装饰的函数。
核心方法
| 方法 | 功能说明 |
|---|---|
GetNodeStatus() | 获取节点状态 |
UpdateNodeStatus() | 更新节点状态 |
GetIsRootBuilderNode() | 检查是否为根 BuilderNode |
SetJsBuilderNodeId() | 设置 JS BuilderNode ID |
SetBuilderFunc() | 设置构建函数 |
GetBuilderFunc() | 获取构建函数 |
SetUpdateNodeFunc() | 设置节点更新函数 |
SetUpdateNodeConfig() | 设置配置更新函数 |
节点状态
enum class NodeStatus : char {
NORMAL_NODE = 0, // 普通节点
BUILDER_NODE_OFF_MAINTREE = 1, // BuilderNode,脱离主树
BUILDER_NODE_ON_MAINTREE = 2 // BuilderNode,挂载到主树
};
工作流程
@Builder 函数调用
↓
创建 BuilderNode(OFF_MAINTREE)
↓
延迟构建(lazyBuilderFunc_)
↓
需要时挂载到主树(ON_MAINTREE)
↓
动态更新(updateNodeFunc_)
设计意图:@Builder 装饰的函数创建的节点可以延迟构建,节点可以脱离主树(OFF_MAINTREE),需要时再挂载(ON_MAINTREE)。
6.3 回调机制(Callback System)
多种回调函数,用于与 JS 层交互和延迟构建。
回调类型
| 回调 | 类型 | 用途 |
|---|---|---|
updateJSInstanceCallback_ | std::function<void(int32_t)> | 实例更新回调 |
lazyBuilderFunc_ | std::function<void()> | 延迟构建函数 |
updateNodeFunc_ | std::function<void(int32_t, RefPtr<UINode>&)> | 节点更新函数 |
updateNodeConfig_ | std::function<void()> | 配置更新函数 |
destroyCallback_ | std::function<void(int32_t)> | 销毁回调 |
使用示例
// 注册回调
node->RegisterUpdateJSInstanceCallback([=](int32_t id) {
// 实例更新逻辑
});
node->SetBuilderFunc([=]() {
// 延迟构建逻辑
});
node->SetOnNodeDestroyCallback([=](int32_t id) {
// 节点销毁逻辑
});
用途:与前端语言 层交互、延迟构建、节点更新通知、生命周期管理。
6.4 内存管理机制
智能指针 + 引用计数,避免泄漏与循环引用。
关键设计
- 父方向用
WeakPtr(不增加父的强引用):WeakPtr<UINode> parent_; // 父节点,使用前需 Upgrade() 判空 WeakPtr<UINode> adoptParent_; // 收养父(如 overlay) WeakPtr<UINode> ancestor_; // 祖先,插入去重等用 - 子方向用
RefPtr(父拥有子):std::list<RefPtr<UINode>> children_; - 释放钩子:强引用归零时由基类
Referenced::DecRefCount()调用,子类可干预是否立即 delete:virtual bool MaybeRelease() override; // true=立即 delete,false=不在这里删(如交给 GC)