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)
相关推荐
麟听科技15 小时前
HarmonyOS 6.0+ APP智能种植监测系统开发实战:农业传感器联动与AI种植指导落地
人工智能·分布式·学习·华为·harmonyos
前端不太难16 小时前
HarmonyOS PC 焦点系统重建
华为·状态模式·harmonyos
空白诗16 小时前
基础入门 Flutter for Harmony:Text 组件详解
javascript·flutter·harmonyos
lbb 小魔仙17 小时前
【HarmonyOS】React Native实战+Popover内容自适应
react native·华为·harmonyos
motosheep18 小时前
鸿蒙开发(四)播放 Lottie 动画实战(Canvas 渲染 + 资源加载踩坑总结)
华为·harmonyos
左手厨刀右手茼蒿18 小时前
Flutter for OpenHarmony 实战:Barcode — 纯 Dart 条形码与二维码生成全指南
android·flutter·ui·华为·harmonyos
lbb 小魔仙18 小时前
【HarmonyOS】React Native of HarmonyOS实战:手势组合与协同
react native·华为·harmonyos
果粒蹬i19 小时前
【HarmonyOS】React Native实战项目+NativeStack原生导航
react native·华为·harmonyos
waeng_luo20 小时前
HarmonyOS 应用开发 Skills
华为·harmonyos
石去皿20 小时前
分布式原生:鸿蒙架构哲学与操作系统演进的范式转移
分布式·架构·harmonyos