OHOS Ace_engine 节点核心类型: UINode

5 阅读6分钟

概述

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 渲染流程

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)