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 { <<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、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)
相关推荐
不才难以繁此生2 小时前
ArkUI 列表性能实战|中式美食菜谱列表如何拆组件、控重绘和稳住滑动体验
harmonyos·arkts·状态管理·foreach·arkui·列表性能·组件拆分
Ww.xh2 小时前
DevEco Studio虚拟机部署指南
华为·harmonyos
yuanlaile2 小时前
前端转 Flutter 踩坑实录:一套可落地 Flutter 鸿蒙 App 完整学习路线,建议收藏!
flutter·harmonyos·flutter开发鸿蒙·跨平台开发app·flutter深入学习
爸爸6192 小时前
08 ArkTS 类与静态方法设计:单例、DAO 与设计令牌
华为·harmonyos·鸿蒙系统
ZZZMMM.zip2 小时前
基于鸿蒙HarmonyOS NEXT开发AI天气助手应用:智能出行新体验与鸿蒙Flutter框架跨端实践
人工智能·flutter·华为·harmonyos·鸿蒙系统
心中有国也有家3 小时前
AtomGit Flutter 鸿蒙客户端:一键记录情绪的极致体验
学习·flutter·华为·harmonyos
ZZZMMM.zip3 小时前
营养方程式-营养餐单规划的HarmonyOS开发实践
人工智能·华为·harmonyos·鸿蒙系统
不才难以繁此生3 小时前
HarmonyOS RDB 实战|中式美食菜谱表设计:食材、步骤与购物清单怎么落库
flutter·harmonyos·rdb·中式美食·本地持久化
心中有国也有家3 小时前
AtomGit Flutter 鸿蒙客户端: 周情绪柱状图
学习·flutter·华为·harmonyos
小雨青年3 小时前
【HarmonyOS 7开发者前瞻】06 HarmonyOS 7 AI 能力接入前,先用业务场景判断优先级
人工智能·华为·harmonyos