b42a0fa1创建于 2月26日历史提交

Layout Framework 知识库

文档版本: v1.0 更新时间: 2026-02-26 源码版本: OpenHarmony ace_engine (master 分支)


📚 目录

  1. 概述
  2. 目录结构
  3. 核心类继承关系
  4. 布局流程详解
  5. 关键函数调用链
  6. 性能优化策略
  7. 调试指南
  8. 常见问题

概述

布局系统定位

Layout Flow (布局流程) 是 OpenHarmony ACE Engine 的核心子系统,负责计算和更新 UI 组件的尺寸与位置。布局系统从垂直同步信号触发开始,通过任务调度、节点测量、布局计算、安全区域扩展,最终将几何数据同步到 Rosen 渲染引擎,形成完整的布局计算管道。

技术架构

分层设计:

┌─────────────────────────────────────────────┐
│  PipelineContext (管道上下文)                 │
│  - FlushVsync 触发                           │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│  UITaskScheduler (任务调度器)                 │
│  - 任务调度和管理                             │
│  - dirty 节点收集                             │
│  - 批量任务执行                               │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│  FrameNode (组件节点)                         │
│  - Measure 测量                               │
│  - Layout 布局                                │
│  - OnLayoutFinish 完成处理                   │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│  LayoutAlgorithm (布局算法)                   │
│  - Measure 测量实现                           │
│  - Layout 布局实现                            │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│  LayoutWrapper (布局包装器)                   │
│  - SafeArea 安全区域处理                      │
│  - Keyboard 键盘避让                          │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│  RosenRenderContext (渲染上下文)              │
│  - SyncGeometryProperties 同步               │
│  - SavePaintRect 保存绘制区域                 │
└─────────────────────────────────────────────┘

核心特性:

  • 异步任务调度: 支持多线程布局计算,提升性能
  • 增量更新: 只处理 dirty 标记的节点,减少计算量
  • 安全区域适配: 支持系统安全区域(刘海屏、圆角等)和键盘避让
  • 几何节点同步: 布局结果自动同步到 Rosen 渲染引擎
  • 多轮布局支持: 支持几何过渡动画等多轮布局场景

代码规模

模块 文件 代码行数(估计) 作用
任务调度 ui_task_scheduler.cpp ~500 行 任务调度和管理
节点布局 frame_node.cpp ~3500 行 组件节点布局实现
布局包装 layout_wrapper.cpp ~850 行 布局数据包装和传递
布局算法 layout_algorithm.cpp ~150 行 布局算法基础实现

目录结构

源码组织

ace_engine/
├── frameworks/
│   ├── core/
│   │   ├── components_ng/
│   │   │   ├── base/
│   │   │   │   └── frame_node.cpp                    # 帧节点,布局执行主体
│   │   │   ├── layout/
│   │   │   │   ├── layout_wrapper.cpp                # 布局包装器
│   │   │   │   ├── layout_algorithm.cpp              # 布局算法基础
│   │   │   │   └── layout_algorithm.h                # 布局算法接口
│   │   │   ├── manager/safe_area/
│   │   │   │   └── safe_area_manager.cpp             # 安全区域管理器
│   │   │   └── pattern/
│   │   │       └── [各组件]/
│   │   │           └── *_layout_algorithm.cpp        # 各组件布局算法
│   │   └── pipeline_ng/
│   │       ├── ui_task_scheduler.cpp                  # UI任务调度器
│   │       └── pipeline_context.cpp                  # 管道上下文
│   └── ...
└── ...

核心文件说明

1. UITaskScheduler

路径: frameworks/core/pipeline_ng/ui_task_scheduler.cpp

职责:

  • 管理布局和渲染任务的调度
  • 收集 dirty 节点
  • 批量执行布局和同步任务
  • 协调任务执行顺序

关键类:

class UITaskScheduler {
    std::vector<RefPtr<FrameNode>> dirtyLayoutNodes_;     // dirty 布局节点
    std::map<int32_t, PageDirtySet> dirtyRenderNodes_;   // dirty 渲染节点
    std::vector<std::function<void()>> syncGeometryNodeTasks_; // 同步任务队列

    void FlushTask();                    // 主任务执行函数
    void FlushLayoutTask();              // 刷新布局任务
    void FlushSyncGeometryNodeTasks();   // 刷新几何节点同步任务
    void FlushRenderTask();              // 刷新渲染任务
};

2. FrameNode

路径: frameworks/core/components_ng/base/frame_node.cpp

职责:

  • 组件节点的布局执行主体
  • 测量和布局的主要执行者
  • 布局完成后的回调处理
  • 几何节点同步

关键方法:

class FrameNode : public UINode {
    void CreateLayoutTask(bool forceUseMainThread, LayoutType layoutTaskType);
    void Measure(const std::optional<LayoutConstraintF>& parentConstraint);
    void Layout();
    bool OnLayoutFinish(bool& needSyncRsNode, DirtySwapConfig& config);
    void SyncGeometryNode(bool needSyncRsNode, const DirtySwapConfig& config);

    RefPtr<LayoutAlgorithmWrapper> GetLayoutAlgorithm(bool needReset);
    LayoutConstraintF GetLayoutConstraint() const;
};

3. LayoutWrapper

路径: frameworks/core/components_ng/layout/layout_wrapper.cpp

职责:

  • 布局数据的包装和传递
  • 安全区域扩展处理
  • 键盘避让处理
  • 布局约束应用

关键方法:

class LayoutWrapper {
    void ApplyConstraint(LayoutConstraintF constraint);
    void ExpandSafeArea();
    bool AvoidKeyboard(bool isFocusOnPage);
    OffsetF GetParentGlobalOffsetWithSafeArea() const;
    RectF GetFrameRectWithSafeArea() const;
};

4. LayoutAlgorithm

路径: frameworks/core/components_ng/layout/layout_algorithm.cpp

职责:

  • 布局算法的基础实现
  • Measure 和 Layout 的包装器层

关键方法:

class LayoutAlgorithm {
    virtual void Measure(LayoutWrapper* layoutWrapper) = 0;
    virtual void Layout(LayoutWrapper* layoutWrapper) = 0;
};

class LayoutAlgorithmWrapper {
    void Measure(LayoutWrapper* layoutWrapper);
    void Layout(LayoutWrapper* layoutWrapper);
};

核心类继承关系

布局相关类继承图

UINode
  │
  ├─ FrameNode (组件节点)
  │    │
  │    ├─ pattern_: Pattern (业务逻辑)
  │    ├─ layoutProperty_: LayoutProperty (布局属性)
  │    ├─ geometryNode_: GeometryNode (几何数据)
  │    ├─ renderContext_: RenderContext (渲染上下文)
  │    └─ layoutAlgorithm_: LayoutAlgorithmWrapper (布局算法)
  │
  └─ ...

LayoutAlgorithm (布局算法基类)
  │
  ├─ FlexLayoutAlgorithm (弹性布局)
  ├─ GridLayoutAlgorithm (网格布局)
  ├─ ScrollLayoutAlgorithm (滚动布局)
  └─ ... (各组件的布局算法)

LayoutWrapper (布局包装器)
  │
  └─ LayoutWrapperNode (节点包装器)
       │
       ├─ hostNode_: FrameNode (宿主节点)
       ├─ geometryNode_: GeometryNode (几何节点)
       └─ layoutProperty_: LayoutProperty (布局属性)

关键数据结构

LayoutConstraintF (布局约束)

struct LayoutConstraintF {
    OptionalSizeF minSize;           // 最小尺寸
    OptionalSizeF maxSize;           // 最大尺寸
    OptionalSizeF percentReference;  // 百分比参考尺寸
    ScaleProperty scaleProperty;     // 缩放属性
};

DirtySwapConfig (变化配置)

struct DirtySwapConfig {
    bool frameSizeChange;      // 框架尺寸是否变化
    bool frameOffsetChange;    // 框架位置是否变化
    bool contentSizeChange;    // 内容尺寸是否变化
    bool contentOffsetChange;  // 内容位置是否变化
    bool skipMeasure;          // 是否跳过测量
    bool skipLayout;           // 是否跳过布局
};

SafeAreaExpandOpts (安全区域扩展选项)

struct SafeAreaExpandOpts {
    LayoutSafeAreaType type;   // 扩展类型(系统/键盘等)
    LayoutSafeAreaEdge edges;  // 扩展边(上/下/左/右)
    bool switchToNone;         // 是否切换到无扩展

    bool Expansive() const;    // 是否需要扩展
    bool ExpansiveToKeyboard() const; // 是否扩展到键盘
};

布局流程详解

完整流程图

┌─────────────────────────────────────────────────────────────┐
│ 1. PipelineContext::FlushVsync                              │
│    接收垂直同步信号,触发 UI 刷新                              │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. UITaskScheduler::FlushTask                               │
│    协调布局和渲染任务的执行顺序                                │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. UITaskScheduler::FlushLayoutTask                         │
│    批量执行所有 dirty 布局节点                                │
│    - 收集 dirty 节点                                         │
│    - 为每个节点创建布局任务                                   │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ├─────────────────────────────────────────┐
                         │                                         │
                         ▼                                         ▼
┌──────────────────────────────────┐             ┌──────────────────────────────┐
│ 4. FrameNode::CreateLayoutTask   │             │ 其他 dirty 节点               │
│    - 更新布局属性标志              │             │ ...                           │
│    - 获取布局约束                  │             └──────────────────────────────┘
└────────────┬─────────────────────┘
             │
             ├──────────────────┐
             │                  │
             ▼                  ▼
┌─────────────────────────┐  ┌──────────────────────────────┐
│ 5. FrameNode::Measure   │  │ 6. FrameNode::Layout         │
│    测量阶段              │  │    布局阶段                   │
│    ├─ 应用约束           │  │    ├─ 调整安全区域            │
│    ├─ 测量内容           │  │    ├─ 执行布局算法            │
│    ├─ 执行算法           │  │    ├─ 布局完成处理            │
│    └─ 应用宽高比         │  │    └─ 添加同步任务            │
└────────┬────────────────┘  └────────┬───────────────────────┘
         │                            │
         │                            ├────────────────────────────────┐
         │                            │                                │
         ▼                            ▼                                ▼
┌─────────────────┐       ┌──────────────────┐       ┌──────────────────────┐
│ MeasureContent  │       │ 算法布局          │       │ OnLayoutFinish       │
│ 测量内容尺寸     │       │ 排列子组件        │       │ 完成回调              │
└─────────────────┘       └──────────┬───────┘       └────────┬─────────────┘
                                      │                        │
                                      │                        ├─────────────┐
                                      │                        │             │
                                      ▼                        ▼             ▼
                            ┌─────────────────┐    ┌──────────────┐  ┌──────────────┐
                            │ LayoutAlgorithm │    │ SavePaintRect│  │ MarkDirty    │
                            │ Layout 方法     │    │ 保存绘制区域  │  │ 标记重绘      │
                            └────────┬────────┘    └──────────────┘  └──────────────┘
                                     │
                                     ▼
                            ┌─────────────────┐
                            │ AddSyncTask     │
                            │ 添加同步任务     │
                            └────────┬────────┘
                                     │
                                     ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 7. UITaskScheduler::FlushSyncGeometryNodeTasks                     │
│    批量执行几何节点同步任务                                           │
│    ├─ ExpandSafeArea 扩展安全区域                                   │
│    ├─ SetLayoutNodeRect 设置节点位置                                │
│    └─ 执行所有同步任务                                              │
└────────────────────────┬────────────────────────────────────────────┘
                         │
                         ├────────────────────┐
                         │                    │
                         ▼                    ▼
┌──────────────────────────────┐  ┌──────────────────────────────────┐
│ 8. SafeAreaManager           │  │ 9. FrameNode::SyncGeometryNode   │
│    ::ExpandSafeArea          │  │    同步几何节点                    │
│    扩展安全区域               │  │    ├─ 同步边框属性                │
│    └─ LayoutWrapper          │  │    ├─ 触发 Pattern 回调           │
│        ::ExpandSafeArea      │  │    ├─ 同步几何属性                │
│            扩展到安全区域     │  │    ├─ 更新背景                    │
└──────────────────────────────┘  │    └─ 重建渲染树                  │
                                  └──────────────────────────────────┘
                                                  │
                                                  ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 10. UITaskScheduler::FlushRenderTask                               │
│     执行渲染任务(送显阶段,非布局核心流程)                             │
└─────────────────────────────────────────────────────────────────────┘

核心函数调用序列

测量阶段调用链

FrameNode::Measure (frame_node.cpp:5535)
  │
  ├─> GetLayoutAlgorithm()           // 获取布局算法
  │    └─> pattern_->CreateLayoutAlgorithm()
  │
  ├─> ApplyConstraint()              // 应用布局约束
  │    └─> layoutProperty_->UpdateLayoutConstraint()
  │
  ├─> layoutAlgorithm_->MeasureContent()  // 测量内容尺寸
  │    └─> [具体算法实现]
  │
  ├─> layoutAlgorithm_->Measure(this)      // 执行测量算法
  │    └─> LayoutAlgorithmWrapper::Measure()
  │         └─> layoutAlgorithm_->Measure(layoutWrapper)
  │
  └─> geometryNode_->SetFrameSize()    // 设置框架尺寸

关键文件引用:

  • frame_node.cpp:5535-5679 - FrameNode::Measure
  • frame_node.cpp:6209-6220 - GetLayoutAlgorithm
  • layout_algorithm.cpp:85-97 - LayoutAlgorithmWrapper::Measure

布局阶段调用链

FrameNode::Layout (frame_node.cpp:5682)
  │
  ├─> OffsetNodeToSafeArea()        // 调整到安全区域
  │
  ├─> layoutAlgorithm_->Layout(this)    // 执行布局算法
  │    └─> LayoutAlgorithmWrapper::Layout()
  │         └─> layoutAlgorithm_->Layout(layoutWrapper)
  │
  ├─> OnLayoutFinish()               // 布局完成处理
  │    ├─> 检测尺寸变化
  │    ├─> renderContext_->SavePaintRect()  // 保存绘制区域
  │    ├─> pattern_->OnDirtyLayoutWrapperSwap()  // Pattern 回调
  │    └─> MarkDirtyNode()           // 标记需要重绘
  │
  └─> pipeline->AddSyncGeometryNodeTask()  // 添加同步任务
       └─> 任务队列存储

关键文件引用:

  • frame_node.cpp:5682-5789 - FrameNode::Layout
  • frame_node.cpp:5860-5965 - FrameNode::OnLayoutFinish
  • layout_algorithm.cpp:99-108 - LayoutAlgorithmWrapper::Layout

同步阶段调用链

UITaskScheduler::FlushSyncGeometryNodeTasks (ui_task_scheduler.cpp:116)
  │
  ├─> ExpandSafeArea()               // 扩展安全区域
  │    └─> SafeAreaManager::ExpandSafeArea()
  │         └─> LayoutWrapper::ExpandSafeArea()
  │              ├─> GetParentGlobalOffsetWithSafeArea()  // 获取父节点偏移
  │              ├─> ExpandHelper()                       // 执行扩展
  │              ├─> AdjustFixedSizeNode()                // 调整固定尺寸节点
  │              ├─> AdjustChildren()                     // 调整子节点
  │              └─> renderContext_->UpdatePaintRect()    // 更新绘制区域
  │
  ├─> SetLayoutNodeRect()           // 设置节点位置
  │
  └─> 执行所有同步任务
       └─> FrameNode::SyncGeometryNode()  // 同步几何节点
            ├─> 更新边框属性
            ├─> pattern_->OnSyncGeometryNode()  // Pattern 回调
            ├─> renderContext_->SyncGeometryProperties()  // 同步几何属性
            ├─> UpdateBackground()       // 更新背景
            └─> RebuildRenderContextTree()  // 重建渲染树

关键文件引用:

  • ui_task_scheduler.cpp:116-127 - FlushSyncGeometryNodeTasks
  • ui_task_scheduler.cpp:107-114 - ExpandSafeArea
  • layout_wrapper.cpp:297-365 - LayoutWrapper::ExpandSafeArea
  • frame_node.cpp:5967-6033 - FrameNode::SyncGeometryNode

组件树的递归执行逻辑

布局系统通过递归遍历组件树来完成所有组件的测量和布局。父容器在执行 Measure/Layout 时,会遍历所有子组件,对每个子组件调用其 Measure/Layout 方法,子组件又会递归地对它的子组件执行相同的操作,形成深度优先的遍历。

递归执行的三层结构

┌─────────────────────────────────────────────────────────────┐
│ FrameNode 层 (frame_node.cpp)                               │
│   - 触发递归: Measure() / Layout()                          │
│   - 调用 LayoutAlgorithm                                    │
│   - 不直接遍历子组件                                         │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ LayoutAlgorithm 层 (flex_layout_algorithm.cpp)              │
│   - 遍历子组件: GetAllChildrenWithBuild()                   │
│   - 创建约束: CreateChildConstraint()                       │
│   - 触发子组件递归: child->Measure() / child->Layout()      │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ LayoutWrapper 层 (layout_wrapper_node.cpp)                  │
│   - 递归入口: Measure() / Layout()                          │
│   - 调用子组件的 LayoutAlgorithm                            │
│   - 形成递归循环                                            │
└─────────────────────────────────────────────────────────────┘

Measure 阶段的递归

执行流程:

FlexLayoutAlgorithm::Measure (父容器)
  │
  ├─> 1. 获取所有子组件
  │     const auto& children = layoutWrapper->GetAllChildrenWithBuild()
  │
  ├─> 2. 计算父容器尺寸
  │     realSize = CreateIdealSizeByPercentRef(...)
  │
  └─> 3. 遍历子组件并递归测量
        for (auto& child : children) {
            // 创建子组件约束
            childConstraint = CreateChildConstraint()

            // 递归调用子组件 Measure
            child->Measure(childConstraint)
                │
                └─> LayoutWrapperNode::Measure (layout_wrapper_node.cpp:150)
                     │
                     ├─> ApplyConstraint(childConstraint)  // 应用约束
                     │
                     ├─> layoutAlgorithm_->MeasureContent()  // 测量内容
                     │
                     ├─> layoutAlgorithm_->Measure(this)     // 执行算法
                     │     │
                     │     └─> 如果子组件是容器: 继续递归遍历其子组件
                     │
                     └─> geometryNode_->SetFrameSize()      // 设置尺寸
        }

例举源码位置:

  • Flex Measure: flex_layout_algorithm.cpp:1106-1177
  • Flex 递归调用: flex_layout_algorithm.cpp:588-629 (MeasureAndCleanMagicNodes)
  • LayoutWrapperNode Measure: layout_wrapper_node.cpp:150-215

Flex 的多次测量场景:

  • 第一轮: 测量无 layoutWeight 的子组件
  • 计算: 根据剩余空间计算 layoutWeight 子组件的分配
  • 第二轮: 测量有 layoutWeight 的子组件,使用分配的空间

Layout 阶段的递归

执行流程:

FlexLayoutAlgorithm::Layout (父容器)
  │
  ├─> 1. 获取所有子组件
  │     const auto& children = layoutWrapper->GetAllChildrenWithBuild(false)
  │
  ├─> 2. 计算布局参数
  │     mainAxisSize_ = GetMainAxisSizeHelper(contentSize, direction_)
  │     remainSpace = mainAxisSize_ - allocatedSize_
  │
  ├─> 3. 放置子组件(设置位置)
  │     PlaceChildren(layoutWrapper, frontSpace, betweenSpace, paddingOffset)
  │     │
  │     └─> 对每个子组件:
  │          childGeometryNode->SetMarginFrameOffset(offset)
  │
  └─> 4. 递归调用子组件 Layout
        for (auto&& child : children) {
            child->Layout()  // stack_layout_algorithm.cpp:30-32
                │
                └─> LayoutWrapperNode::Layout (layout_wrapper_node.cpp:218)
                     │
                     ├─> OffsetNodeToSafeArea()  // 调整安全区域
                     │
                     ├─> layoutAlgorithm_->Layout(this)  // 执行布局算法
                     │     │
                     │     └─> 如果子组件是容器: 继续递归遍历其子组件
                     │
                     └─> 返回
        }

例举源码位置:

  • Flex Layout: flex_layout_algorithm.cpp:1240-1299
  • Stack Layout: stack_layout_algorithm.cpp:26-33
  • LayoutWrapperNode Layout: layout_wrapper_node.cpp:218-249

典型组件递归对比

特性 Flex Stack
Measure 递归 父容器遍历子组件递归调用 父容器不递归,子组件独立测量
Layout 递归 放置子组件后递归调用 放置子组件后递归调用
约束传递 向子组件传递约束 不传递约束
测量次数 可能多次(flexWeight) 通常一次

递归执行示例

Column (父容器)
  ├─ Text (子组件1)
  ├─ Row (子组件2)
  │    ├─ Button (孙组件1)
  │    └─ Image (孙组件2)
  └─ Divider (子组件3)

Measure 递归顺序:
1. Column::Measure 开始
2.   Text::Measure 开始 → 结束
3.   Row::Measure 开始
4.     Button::Measure 开始 → 结束
5.     Image::Measure 开始 → 结束
6.   Row::Measure 结束
7.   Divider::Measure 开始 → 结束
8. Column::Measure 结束

Layout 递归顺序:
1. Column::Layout 开始(设置所有子组件位置)
2.   Text::Layout 开始 → 结束
3.   Row::Layout 开始(设置 Button 和 Image 位置)
4.     Button::Layout 开始 → 结束
5.     Image::Layout 开始 → 结束
6.   Row::Layout 结束
7.   Divider::Layout 开始 → 结束
8. Column::Layout 结束

递归性能优化

  1. SkipMeasure/SkipLayout: 约束未变化时跳过
  2. 增量布局: 只对变化的子组件递归执行
  3. 懒构建: 使用 GetAllChildrenWithBuild(false) 只获取已构建的子组件
  4. 测量缓存: 缓存子组件的测量结果

关键函数调用链

完整调用路径

PipelineContext::FlushVsync
  └─> UITaskScheduler::FlushTask
      ├─> UITaskScheduler::FlushLayoutTask
      │   ├─> FrameNode::CreateLayoutTask
      │   │   ├─> FrameNode::Measure
      │   │   │   ├─> FrameNode::GetLayoutAlgorithm
      │   │   │   │   └─> Pattern::CreateLayoutAlgorithm
      │   │   │   ├─> LayoutAlgorithmWrapper::Measure
      │   │   │   │   └─> LayoutAlgorithm::Measure
      │   │   │   └─> GeometryNode::SetFrameSize
      │   │   └─> FrameNode::Layout
      │   │       ├─> LayoutAlgorithmWrapper::Layout
      │   │       │   └─> LayoutAlgorithm::Layout
      │   │       ├─> FrameNode::OnLayoutFinish
      │   │       │   ├─> RosenRenderContext::SavePaintRect
      │   │       │   ├─> Pattern::OnDirtyLayoutWrapperSwap
      │   │       │   └─> FrameNode::MarkDirtyNode
      │   │       └─> PipelineContext::AddSyncGeometryNodeTask
      │   └─> UITaskScheduler::FlushSyncGeometryNodeTasks
      │       ├─> UITaskScheduler::ExpandSafeArea
      │       │   └─> SafeAreaManager::ExpandSafeArea
      │       │       └─> LayoutWrapper::ExpandSafeArea
      │       └─> FrameNode::SyncGeometryNode
      │           └─> RosenRenderContext::SyncGeometryProperties
      └─> UITaskScheduler::FlushRenderTask

函数角色定位表

函数 层次 角色定位 核心职责
FlushVsync 触发层 流程触发者 接收垂直同步信号,启动刷新流程
FlushTask 调度层 任务协调者 协调布局和渲染任务的执行顺序
FlushLayoutTask 调度层 批量执行者 批量执行所有 dirty 节点的布局
CreateLayoutTask 执行层 任务创建者 为单个节点创建布局任务
Measure 执行层 测量执行者 计算组件的理想尺寸
GetLayoutAlgorithm 执行层 算法获取者 获取或创建布局算法
LayoutAlgorithm::Measure 算法层 测量算法 具体的测量算法实现
Layout 执行层 布局执行者 计算组件位置并排列子组件
LayoutAlgorithm::Layout 算法层 布局算法 具体的布局算法实现
OnLayoutFinish 执行层 完成处理者 布局完成后的回调处理
SavePaintRect 渲染层 区域保存者 保存绘制区域到渲染上下文
AddSyncGeometryNodeTask 调度层 任务收集者 添加同步任务到队列
FlushSyncGeometryNodeTasks 调度层 批量同步者 批量执行几何节点同步任务
ExpandSafeArea 功能层 区域扩展者 扩展组件到安全区域
SyncGeometryNode 同步层 节点同步者 同步几何数据到渲染引擎
SyncGeometryProperties 同步层 属性同步者 同步几何属性到 RS 节点
FlushRenderTask 渲染层 渲染执行者 执行渲染任务(送显)

性能优化策略

1. Dirty 标记优化

原理: 只处理标记为 dirty 的节点

实现位置: frame_node.cpp:2545-2547

if (!isLayoutDirtyMarked_ && (layoutTaskType == LayoutType::NONE)) {
    return;  // 非 dirty 节点跳过
}

优化效果:

  • 减少不必要的布局计算
  • 降低 CPU 占用
  • 提升布局响应速度

触发条件:

  • 节点属性变化时会标记 isLayoutDirtyMarked_ = true
  • 父节点 dirty 可能触发子节点 dirty

2. 约束未变化优化

原理: 约束未变化且未强制测量时跳过测量

实现位置: frame_node.cpp:5606-5618

isConstraintNotChanged_ = layoutProperty_->ConstraintEqual(preConstraint, contentConstraint);

if (isConstraintNotChanged_) {
    if (!CheckNeedForceMeasureAndLayout()) {
        layoutAlgorithm_->SetSkipMeasure();
        return;  // 跳过测量
    }
}

优化效果:

  • 避免重复测量
  • 减少算法执行次数
  • 提升布局效率

判断依据:

  • 当前约束与上次约束相等
  • 无强制测量标志

3. SkipMeasure 和 SkipLayout

原理: 分别控制测量和布局阶段的跳过

实现位置: frame_node.cpp:5572-5577, 5708-5745

// 测量阶段
if (layoutAlgorithm_->SkipMeasure()) {
    isLayoutDirtyMarked_ = false;
    return;  // 跳过测量
}

// 布局阶段
if (!CheckNeedLayout(layoutProperty_->GetPropertyChangeFlag())) {
    layoutAlgorithm_->SetSkipLayout();
    return;  // 跳过布局
}

优化效果:

  • 测量和布局可以独立优化
  • 细粒度控制布局流程
  • 最大化复用已有结果

使用场景:

  • 约束未变化 → SkipMeasure
  • 尺寸未变化 → SkipLayout
  • 子组件未变化 → SkipLayout

4. 安全区域扩展缓存

原理: 使用缓存避免重复计算安全区域扩展

实现位置: layout_wrapper.cpp:376-420

bool LayoutWrapper::AccumulateExpandCacheHit(ExpandEdges& totalExpand, ...)
{
    auto& selfAccumulateExpand = geometryNode->GetAccumulatedSafeAreaExpand();
    if (selfAccumulateExpand->OptionalValueCover(totalExpand)) {
        // 使用缓存结果
        totalExpand += *selfAccumulateExpand;
        return true;
    }
    return false;
}

优化效果:

  • 减少安全区域计算开销
  • 避免重复遍历父节点链
  • 提升布局性能

缓存条件:

  • 父节点已有缓存覆盖子节点扩展方向
  • PaddingBorder 为零

5. 批量同步优化

原理: 批量执行同步任务减少开销

实现位置: ui_task_scheduler.cpp:116-127

void UITaskScheduler::FlushSyncGeometryNodeTasks()
{
    auto tasks = std::move(syncGeometryNodeTasks_);
    for (auto& task : tasks) {
        if (task) {
            task();  // 批量执行
        }
    }
}

优化效果:

  • 减少任务调度开销
  • 提高执行效率
  • 便于优化同步策略

优势:

  • 任务集中执行,减少上下文切换
  • 便于实现同步策略优化
  • 支持任务优先级排序

6. 增量布局

原理: 只布局变化的子组件

实现: 组件级别支持,由各 Pattern 实现

示例: Flex 组件的增量布局

优化效果:

  • 大幅减少布局计算量
  • 提升复杂场景性能
  • 降低布局耗时

适用场景:

  • 列表滚动
  • 动态内容更新
  • 局部刷新

调试指南

调试追踪开关

1. 布局追踪

位置: frame_node.cpp:5540

ACE_LAYOUT_TRACE_BEGIN("Measure[%s][self:%d][parent:%d][key:%s]", ...);
// ... 布局代码
ACE_LAYOUT_TRACE_END()

使用方法: 编译时启用 ACE_LAYOUT_TRACE

输出内容:

  • 节点标签、ID、父节点ID、Inspector ID
  • 测量和布局耗时
  • 约束信息

2. 测量调试追踪

位置: frame_node.cpp:5542-5548

if (SystemProperties::GetMeasureDebugTraceEnabled()) {
    ACE_MEASURE_SCOPED_TRACE("MeasureInfo[frameRect:%s][parentConstraint:%s][calcConstraint:%s]",
        GetGeometryNode()->GetFrameRect().ToString().c_str(),
        parentConstraint.has_value() ? parentConstraint.value().ToString().c_str() : "NA",
        layoutProperty_->GetCalcLayoutConstraint() ?
            layoutProperty_->GetCalcLayoutConstraint()->ToString().c_str() : "NA");
}

使用方法: 运行时设置 SystemProperties::GetMeasureDebugTraceEnabled() = true

输出内容:

  • 框架矩形信息
  • 父节点约束信息
  • 计算约束信息

3. 同步调试追踪

位置: frame_node.cpp:5969-5974

if (SystemProperties::GetSyncDebugTraceEnabled()) {
    ACE_LAYOUT_TRACE_BEGIN("SyncGeometryNode[%s][self:%d][parent:%d][key:%s][paintRect:%s][needSyncRsNode:%d]",
        tag_.c_str(), nodeId_, ..., renderContext_->GetPaintRectWithoutTransform().ToString().c_str(), needSyncRsNode);
    ACE_LAYOUT_TRACE_END()
}

使用方法: 运行时设置 SystemProperties::GetSyncDebugTraceEnabled() = true

输出内容:

  • 同步节点信息
  • 绘制矩形信息
  • 是否需要同步 RS 节点

4. 安全区域调试追踪

位置: layout_wrapper.cpp:289-294

if (SystemProperties::GetSafeAreaDebugTraceEnabled()) {
    ACE_SAFE_AREA_SCOPED_TRACE("ExpandSafeAreaFinish[%s][self:%d][parent:%d][key:%s][paintRectRect:%s][selfAdjust:%s]",
        host->GetTag().c_str(), host->GetId(), ..., renderContext->GetPaintRectWithoutTransform().ToString().c_str(),
        selfAdjust.ToString().c_str());
}

使用方法: 运行时设置 SystemProperties::GetSafeAreaDebugTraceEnabled() = true

输出内容:

  • 安全区域扩展信息
  • 绘制矩形变化
  • 自调整量信息

常用调试命令

查看 dirty 节点

# 添加日志到 UITaskScheduler::FlushLayoutTask
# 查看有多少 dirty 节点

追踪单个节点的布局

# 在 FrameNode::Measure/Layout 开始处添加条件断点
# 条件: nodeId_ == <目标节点ID>

查看布局约束

# 在 FrameNode::Measure 中查看 parentConstraint
# 在 FrameNode::Layout 中查看 layoutProperty_->GetLayoutConstraint()

查看安全区域扩展

# 在 LayoutWrapper::ExpandSafeArea 中添加日志
# 查看 opts、frame、selfAdjust 等关键变量

性能分析

布局耗时分析

工具: ACE_LAYOUT_TRACE

方法:

  1. 启用布局追踪
  2. 查看日志输出
  3. 分析各阶段耗时

关键指标:

  • Measure 耗时
  • Layout 耗时
  • SyncGeometryNode 耗时
  • ExpandSafeArea 耗时

布局次数分析

工具: 日志统计

方法:

  1. FlushTask 中添加计数器
  2. 统计每帧的布局次数
  3. 分析是否存在过多次布局

异常情况:

  • 单帧布局次数 > 2
  • 某个节点反复布局
  • 布局循环

内存占用分析

工具: 内存分析工具

关键指标:

  • LayoutWrapper 数量
  • GeometryNode 数量
  • LayoutAlgorithm 数量

优化方向:

  • 及时清理过期对象
  • 复用 LayoutAlgorithm
  • 减少不必要的 GeometryNode 创建

常见问题

Q1: 为什么需要多次布局?

场景:

  • 几何过渡动画: 需要在动画过程中多次布局以更新位置
  • 增量布局: 第一次布局可能触发子组件需要重新布局
  • 安全区域扩展: 扩展后可能需要重新布局相关节点

实现机制:

void UITaskScheduler::FlushTask()
{
    do {
        FlushLayoutTask();
        if (NeedAdditionalLayout()) {
            FlushLayoutTask();
        }
        layoutedCount++;
        multiLayoutCount_--;
    } while (multiLayoutCount_ > 0);
}

控制策略:

  • 最多执行 ENDORSE_LAYOUT_COUNT(2) 次后请求下一帧继续
  • 通过 multiLayoutCount_ 控制布局次数
  • 避免无限循环

Q2: SkipMeasure 和 SkipLayout 的区别?

区别:

特性 SkipMeasure SkipLayout
跳过阶段 测量阶段 布局阶段
复用内容 复用测量结果 复用布局结果
触发条件 约束未变化 尺寸未变化
影响范围 不影响子组件测量 不影响子组件布局

独立性: 两者可以独立触发,实现细粒度优化

使用场景:

  • 约束未变化 → SkipMeasure
  • 尺寸未变化 → SkipLayout
  • 两者同时满足 → 完全跳过布局流程

Q3: 安全区域扩展何时执行?

执行时机:

1. 所有节点的 Measure 和 Layout 完成后
2. 在 FlushSyncGeometryNodeTasks 阶段
3. 在同步几何节点之前

原因:

  1. 统一处理: 在统一的时机处理所有需要扩展的节点
  2. 避免重复: 避免重复计算和同步
  3. 最终位置: 确保同步的位置是最终位置(包括安全区域扩展)

执行流程:

FlushLayoutTask (完成所有测量和布局)
  └─> FlushSyncGeometryNodeTasks
       ├─> ExpandSafeArea (统一扩展安全区域)
       └─> 执行同步任务 (同步最终位置)

Q4: 几何节点同步任务为什么要延迟执行?

原因:

  1. 批量处理: 可以批量处理多个节点的同步任务,减少开销
  2. 避免重复: 如果节点在后续布局中再次变化,避免重复同步
  3. 安全区域: 先统一扩展安全区域,再同步,确保同步的位置是最终位置
  4. 性能优化: 减少同步次数,提升性能

实现机制:

// FrameNode::Layout
auto task = [weak = WeakClaim(this), needSync, dirtyConfig]() {
    auto frameNode = weak.Upgrade();
    CHECK_NULL_VOID(frameNode);
    frameNode->SyncGeometryNode(needSync, dirtyConfig);
};
pipeline->AddSyncGeometryNodeTask(task);  // 添加到队列

// UITaskScheduler::FlushTask
FlushLayoutTask();  // 先完成所有布局
FlushSyncGeometryNodeTasks();  // 再批量同步

Q5: 如何判断一个节点是否需要同步到渲染引擎?

判断位置: FrameNode::OnLayoutFinish

判断条件:

needSyncRsNode = frameSizeChange || frameOffsetChange ||
                 contentSizeChange || HasPositionProp() || SelfOrParentExpansive();

包括以下情况:

  • 框架尺寸变化: frameSizeChange
  • 框架位置变化: frameOffsetChange
  • 内容尺寸变化: contentSizeChange (有上下文参数时)
  • 有位置属性: HasPositionProp()
  • 自身或父节点需要扩展到安全区域: SelfOrParentExpansive()

优化策略:

  • 只在必要时同步
  • 避免无意义的同步开销
  • 提升渲染性能

Q6: LayoutWrapper 和 FrameNode 的关系?

关系:

特性 FrameNode LayoutWrapper
定位 组件节点的完整表示 布局数据的包装器
包含内容 Pattern, Property, EventHub, GeometryNode 等 只包含布局相关的数据
生命周期 组件存在期间一直存在 只在布局期间创建
内存占用 较大 较小(轻量级)

使用场景:

  • FrameNode: 组件的完整生命周期管理
  • LayoutWrapper: 布局流程中的数据传递

优势:

  • 减少布局过程中的数据依赖
  • 提供布局算法的统一接口
  • 优化布局性能

Q7: 如何添加自定义布局算法?

步骤:

  1. 创建布局算法类
class MyLayoutAlgorithm : public LayoutAlgorithm {
public:
    void Measure(LayoutWrapper* layoutWrapper) override {
        // 测量逻辑
    }

    void Layout(LayoutWrapper* layoutWrapper) override {
        // 布局逻辑
    }
};
  1. 在 Pattern 中创建算法
class MyPattern : public Pattern {
    RefPtr<LayoutAlgorithm> CreateLayoutAlgorithm() override {
        return MakeRefPtr<MyLayoutAlgorithm>();
    }
};
  1. 框架自动调用
  • FrameNode::Measure 会调用 GetLayoutAlgorithm
  • GetLayoutAlgorithm 会调用 Pattern::CreateLayoutAlgorithm
  • 自动执行算法的 Measure 和 Layout 方法

Q8: 如何调试布局问题?

调试步骤:

  1. 启用调试追踪
SystemProperties::SetMeasureDebugTraceEnabled(true);
SystemProperties::SetSyncDebugTraceEnabled(true);
  1. 查看日志输出
  • Measure 日志: 查看约束和尺寸
  • Layout 日志: 查看位置信息
  • Sync 日志: 查看同步信息
  1. 添加断点
  • FrameNode::Measure: 测量阶段
  • FrameNode::Layout: 布局阶段
  • FrameNode::OnLayoutFinish: 完成阶段
  1. 检查关键数据
  • 布局约束: LayoutProperty::GetLayoutConstraint()
  • 测量结果: GeometryNode::GetFrameSize()
  • 布局结果: GeometryNode::GetFrameOffset()

附录

相关文档

源码路径汇总

文件 路径 作用
任务调度器 frameworks/core/pipeline_ng/ui_task_scheduler.cpp 任务调度和管理
帧节点 frameworks/core/components_ng/base/frame_node.cpp 组件节点布局执行主体
布局包装器 frameworks/core/components_ng/layout/layout_wrapper.cpp 布局数据包装和传递
布局算法 frameworks/core/components_ng/layout/layout_algorithm.cpp 布局算法基础实现
管道上下文 frameworks/core/pipeline_ng/pipeline_context.cpp 管道上下文,触发布局

关键类汇总

类名 头文件 作用
UITaskScheduler ui_task_scheduler.h UI 任务调度器
FrameNode frame_node.h 组件节点
LayoutWrapper layout_wrapper.h 布局包装器
LayoutAlgorithm layout_algorithm.h 布局算法基类
LayoutAlgorithmWrapper layout_algorithm.h 布局算法包装器
GeometryNode geometry_node.h 几何节点
LayoutProperty layout_property.h 布局属性
SafeAreaManager safe_area_manager.h 安全区域管理器

布局相关宏定义

宏名称 作用 定义位置
ACE_LAYOUT_TRACE 布局追踪 ace_trace.h
ACE_SCOPED_TRACE 作用域追踪 ace_trace.h
ENDORSE_LAYOUT_COUNT 最大布局次数 ui_task_scheduler.cpp

文档维护: 本文档应随代码变更同步更新,确保准确反映当前实现。