OHOS Ace_engine 节点核心类型: UINode

概述

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 { <> +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、RenderContext
  • SyntaxNode 系列:语法控制节点(ForEach、IfElse、WithTheme 等)

第四层:特殊节点层

  • GroupNode:组合节点,用于特殊布局场景

关键设计模式

  1. 组合模式UINode 通过 children_ 管理子节点,形成树结构
  2. 模板方法UINode 定义骨架,子类实现具体行为(如 IsAtomicNode()
  3. 观察者模式 :通过 PipelineContext 管理节点生命周期
  4. 策略模式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 渲染流程

scss 复制代码
PipelineContext (渲染上下文)
    ↓
UINode Tree (节点树)
    ├── FrameNode (实际渲染节点)
    │   ├── Pattern (行为策略)
    │   ├── LayoutProperty (布局属性)
    │   └── RenderContext (渲染上下文)
    └── SyntaxNode (语法控制节点)
        └── 管理子 FrameNode
    ↓
LayoutWrapper (布局计算)
    ↓
RenderNode (渲染节点)

5. 核心函数

5.1 节点创建

cpp 复制代码
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 节点销毁

cpp 复制代码
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_:挂载到主树后需要执行的任务列表

使用场景

cpp 复制代码
// 典型场景: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() 设置配置更新函数

节点状态

cpp 复制代码
enum class NodeStatus : char {
    NORMAL_NODE = 0,                    // 普通节点
    BUILDER_NODE_OFF_MAINTREE = 1,      // BuilderNode,脱离主树
    BUILDER_NODE_ON_MAINTREE = 2        // BuilderNode,挂载到主树
};

工作流程

css 复制代码
@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)> 销毁回调

使用示例

cpp 复制代码
// 注册回调
node->RegisterUpdateJSInstanceCallback([=](int32_t id) {
    //  实例更新逻辑
});

node->SetBuilderFunc([=]() {
    // 延迟构建逻辑
});

node->SetOnNodeDestroyCallback([=](int32_t id) {
    // 节点销毁逻辑
});

用途:与前端语言 层交互、延迟构建、节点更新通知、生命周期管理。

6.4 内存管理机制

智能指针 + 引用计数,避免泄漏与循环引用。

关键设计

  • 父方向用 WeakPtr (不增加父的强引用):

    cpp 复制代码
    WeakPtr<UINode> parent_;       // 父节点,使用前需 Upgrade() 判空
    WeakPtr<UINode> adoptParent_;  // 收养父(如 overlay)
    WeakPtr<UINode> ancestor_;     // 祖先,插入去重等用
  • 子方向用 RefPtr (父拥有子):

    cpp 复制代码
    std::list<RefPtr<UINode>> children_;
  • 释放钩子 :强引用归零时由基类 Referenced::DecRefCount() 调用,子类可干预是否立即 delete:

    cpp 复制代码
    virtual bool MaybeRelease() override;  // true=立即 delete,false=不在这里删(如交给 GC)
相关推荐
讯方洋哥22 分钟前
HarmonyOS App开发——鸿蒙ArkTS端云一体化的云函数实现机制
harmonyos
木斯佳13 小时前
HarmonyOS 6 三方SDK对接:从半接模式看Share Kit原理——系统分享的运行机制与设计理念
设计模式·harmonyos·架构设计·分享·半接模式
被温水煮的青蛙14 小时前
HarmonyOS openCustomDialog 实战:从入门到理解原理
harmonyos
高一学习c++会秃头吗15 小时前
鸿蒙适应式布局和响应式布局零基础
harmonyos
HwJack2015 小时前
HarmonyOS应用开发中EmbeddedUIExtensionAbility:跨进程 UI 嵌入的“幕后导演“
ui·华为·harmonyos
早點睡39017 小时前
ReactNative项目鸿蒙化三方库集成实战:react-native-calendars(日历展开和日程模块存在兼容性问题)
react native·react.js·harmonyos
云和数据.ChenGuang21 小时前
鸿蒙 + ChromaDB:端侧向量检索,打造全场景智能应用新范式
华为·harmonyos·鸿蒙
前端不太难21 小时前
AI + 鸿蒙游戏,会不会是下一个爆点?
人工智能·游戏·harmonyos
Gorit1 天前
如何使用 Flutter 开发 HarmonyOS 应用
flutter·华为·harmonyos
键盘鼓手苏苏1 天前
Flutter 三方库 p2plib 的鸿蒙化适配指南 - 实现高性能的端到端(P2P)加密通讯、支持分布式节点发现与去中心化数据流传输实战
flutter·harmonyos·鸿蒙·openharmony