鸿蒙小助手 2026-10-21 14:51:08 发布本原创文章帖发布在华为开发者联盟社区,欢迎开发者前往访问评论交流,更多与该内容相关讨论,请点击原帖查看:
RN 性能调优之基础定界与 trace 分析介绍-华为开发者话题 | 华为开发者联盟
本文是 RN 性能定界定位的基础篇,面向 RNOH 应用开发者,介绍三方面内容:
1. RNOH 的线程模型,以及一次页面更新经过的环节;
2. trace 的采集与查看方法;
3. RNOH 在 trace 中输出的关键标记及其含义。
完成基础篇阅读后,如需深入定位具体问题,请继续阅读:
名词说明
本文会反复用到以下名词,含义见下表,阅读过程中可随时查阅:
| 名词 | 解释 |
| RNInstance | 一个 RN 运行实例(含 JS 运行时和渲染环境),一个应用可以同时存在多个 |
| Surface | 由 RN 渲染的独立显示区域,通常对应一个页面、弹层或卡片 |
| Fabric | React Native 新架构的渲染系统,负责影子树的创建、布局、对比和挂载 |
| 影子树(ShadowTree) | 组件树在ArkUI侧的中间表示,布局与对比先在影子树上完成,差异再应用到真实界面 |
| mutations | 新旧影子树对比后生成的变更指令列表(详见 §1.2) |
| TurboModule | JS 调用ArkUI能力的模块(网络、文件、传感器等) |
| ArkUI | 鸿蒙UI 框架,RN 的组件最终由它创建和绘制 |
| Render Service | 系统合成服务,负责将各应用的画面合成到屏幕上 |
| App Frame / RS Frame | 性能工具中的两条帧泳道:应用产帧 / 系统合成帧 |
| trace | 性能分析记录,按时间轴展示各线程在各时间段的执行内容 |
1. RNOH 线程模型与渲染链路
1.1 四线程模型
RNOH 内部有四类线程,各自承担不同职责:
| 线程 | 职责 | QoS 优先级 | trace 中如何识别 |
| MAIN/UI | 应用主线程(也是 UI 线程),每个应用进程只有一个。负责 ArkUI 组件生命周期管理(CREATE/UPDATE/INSERT/REMOVE/DELETE)、组件树管理、TurboModule 业务执行、交互事件处理 | 跟随系统主线程 | 主线程名不会被改成 RNOH_MAIN(复用主线程),需通过"应用主线程 + RNOH 调用栈"确认 |
| JS | JS 运行时线程,通过 Hermes 执行 React 代码。负责 bundle 加载、ShadowTree 创建、Yoga 布局计算、新老树 diff 生成 mutations、交互事件处理。每个 RNInstance 绑定一个独立 JS 线程 | QOS_USER_INTERACTIVE(用户交互级,与 UI 渲染同档的高优先级) | 搜索线程名 RNOH_JS |
| BACKGROUND | 实验特性,开启后会将 JS 线程的部分布局、ShadowTree 比较任务迁移到该线程,降低 JS 线程负荷。由于涉及复杂线程通信,稳定性有风险,正式商用版本不要开启 | 未单独设置 | 开启后搜索线程名 RNOH_BACKGROUND;默认不创建该线程 |
| WORKER | TurboModule 工作线程,将 TurboModule 运行在独立线程,避免与主线程 UI 绘制竞争资源。需要在继承 RNAbility 后重写 getRNOHWorkerScriptUrl 方法配置 | 跟随 Worker 线程默认 | 搜索 WorkerThread |
线程之间的通信方向:JS → Main 通过任务投递进入主线程事件循环;Main → JS 通过消息队列投递到 JS 线程。
下图是一次典型操作中各线程的 trace 示例(线程号 53130 为 MAIN 线程,53214/53216 为两个 JS 线程实例):

1.2 渲染三阶段
RNOH 的渲染流程继承自 React Native 新架构,分为三个阶段,分布在不同线程执行:
• 渲染(Render):在 JS 线程中,React 执行业务代码创建 React 元素树(React Element Tree),然后通过 JSI 在 C++ 中创建 React 影子树(React Shadow Tree)。
• 提交(Commit):影子树创建完成后,通过 Yoga 引擎进行布局计算,然后与上一次的影子树对比,生成变更指令列表(mutations)。
• 挂载(Mount):mutations 被提交到主线程,转化为 ArkUI 组件树操作(CREATE/INSERT/UPDATE/REMOVE/DELETE),最终由 ArkUI 完成绘制和合成。

下图以一次点击跳转到 1500 个 Text 组件页面为例,展示 trace 中的渲染三阶段实例:

关于 mutations:mutations 是 Fabric 对新旧影子树做 diff 后生成的变更指令列表,每条指令描述一个组件操作(如创建 tag=42 的 Text 组件、更新 tag=10 的 View 属性、将 tag=5 插入到 tag=3 下)。主线程逐条执行这些指令来完成界面更新。
1.3 性能链路
一次 RN 页面更新通常跨越以下环节。图中标注了 ①~⑥ 序号,与 §1.2"渲染三阶段 trace 实例"图(点击跳转到 1500 个 Text 组件页面)中的标注一一对应:

各环节与图中序号、渲染三阶段、trace 标记的对应关系(trace 标记的含义见 §3):
| 执行环节 | 图中序号 | 渲染三阶段 | 所在线程 | 对应 trace 标记 |
| 手指点击、输入分发到 RN Surface | ① | 输入 | RNOH_MAIN | #RNOH::ArkUISurface::onTouchEvent::DOWN/UP |
| JS 线程响应事件、执行 React 业务代码 | ② | 渲染(Render) | RNOH_JS | #RNOH::MessageQueueThread::runOnQueue |
| 通过 JSI 创建影子树 | ③ | 渲染(Render) | RNOH_JS | ReactMarker::FABRIC_COMMIT、cloneNode 等节点树操作 |
| Yoga 布局计算 | ④ | 提交(Commit) | RNOH_JS | ReactMarker::FABRIC_LAYOUT、#RNOH::TextMeasurer::measure |
| 新旧影子树对比,生成 mutations | ⑤ | 提交(Commit) | RNOH_JS | ReactMarker::FABRIC_DIFF |
| mutations 送主线程启动渲染 | ⑥ | 提交(Commit)收尾 | RNOH_JS → RNOH_MAIN | ReactMarker::FABRIC_FINISH_TRANSACTION(主线程开始处理事务) |
| mutations 挂载(CREATE/INSERT/UPDATE/REMOVE/DELETE) | - | 挂载(Mount) | RNOH_MAIN | ReactMarker::FABRIC_BATCH_EXECUTION、#RNOH::MountingManager::doMount |
| ArkUI 布局/绘制、Render Service 合成上屏 | - | 挂载(Mount)之后 | RNOH_MAIN / Render Service | FlushVsync 等系统 trace |
图中 ①~⑥ 的逐步说明也可参考渲染三阶段。
RNOH_MAIN 就是应用主线程,RNOH 在其事件循环上注册回调,不创建新线程。在 trace 中通常需要通过应用主线程身份和 RNOH 调用栈确认,不能只按线程名匹配。RNOH_JS 是独立线程,但线程名也只是一条线索,最终仍需用调用栈和时间关系确认。
1.4 指标定义
在分析 trace 前,需要统一以下指标的定义和测量方法。
卡顿和丢帧
帧预算由问题窗口内的实际目标刷新率决定:
单帧预算(ms) = 1000 / 目标刷新率(Hz)例如,60 Hz、90 Hz、120 Hz 对应的理论预算约为 16.7 ms、11.1 ms、8.3 ms。设备可能动态切换刷新率,因此不能固定使用 16.7 ms,也不能仅用"FPS 低于 60"判断丢帧。应在 DevEco Profiler 中读取 Display、H:PreferredFrameRate 或等价信息,并结合 App Frame、RS Frame 判断。
响应时延
响应时延描述输入事件到首个可见反馈之间的时间,其数值与起止点的选取直接相关:
• 起点:系统工具通常取点击事件上报时刻,而不是物理触屏时刻;
• 终点:通常取首个响应帧上屏。
因此报告中必须记录所用工具和起止点定义。不同工具、不同版本对起止点的取法可能不同,不能把 RNOH 内部的触摸 trace 直接当作端到端响应时延。
完成时延
完成时延描述操作开始到目标页面达到工具所定义"完成状态"的时间。不同测试工具可能以页面切换后的目标帧、动画结束或占位内容加载完成作为终点。业务还可能把"关键内容可见"定义为完成。测试基线、问题复现和优化前后对比必须使用同一终点。
RNOH 的 CONTENT_APPEARED 表示根节点挂载到 ArkUI 内容容器(NodeContent);ROOT_VIEW_TTI 计算为从 APP_STARTUP_START 到 CONTENT_APPEARED 的耗时。它们是 RNOH 内部阶段标记,不证明像素已经上屏、页面内容完整,也不证明页面已经可交互。
HarmonyOS 指标和工具限制请以性能分析与
2. trace 采集与查看
2.1 RNOH 两类 trace 点
RNOH 在 trace 中留下的标记分为两类,粒度和用途完全不同:
| 维度 | RNOHMarker(阶段级) | TraceSection(函数级) |
| trace 中搜索 | ReactMarker:: | #RNOH:: |
| 粒度 | 标记一个完整阶段的起止(如 Bundle 加载开始→结束) | 标记单个函数或代码块的执行区间 |
| 典型时间跨度 | 大(数十毫秒到数秒) | 小(微秒到毫秒级) |
| 是否跨线程 | 可以(起点和终点可以在不同线程) | 不可以(限同一线程内) |
| 编译开关 | WITH_HITRACE_REACT_MARKER | WITH_HITRACE_SYSTRACE |
两类标记的用途可以这样区分:分析"某个阶段花了多久"(如 Bundle 加载、启动)时查看 RNOHMarker,分析"哪个函数耗时"(如挂载、触摸分发)时查看 TraceSection。两类标记的具体含义见 §3。
2.2 开启 trace 编译开关
两个编译宏默认关闭,需要手动开启:
| 编译宏 | 控制的 trace 类型 | 默认值 |
| WITH_HITRACE_REACT_MARKER | ReactMarker::* 异步 trace(阶段标记) | OFF |
| WITH_HITRACE_SYSTRACE | #RNOH::* 同步 trace(函数标记)+ JS 侧 nativeTrace* API | OFF |
注意:开关需要在 CMake 引入 RNOH 目录(add_subdirectory)之前设置,才能对 RNOH 源码生效。
三种开启方式(任选其一,普通应用工程用方式 A 即可):
方式 A:应用 entry 的 CMakeLists.txt 中设置(应用工程推荐,模板默认已开启)
set(WITH_HITRACE_SYSTRACE ON)
set(WITH_HITRACE_REACT_MARKER ON)方式 B:build-profile.json5 的 externalNativeOptions.arguments 中传递(不想改 CMakeLists.txt 时使用,配置随工程走)
"externalNativeOptions": {
"arguments": [
"-DWITH_HITRACE_SYSTRACE=ON",
"-DWITH_HITRACE_REACT_MARKER=ON"
]
}方式 C:第三方工程 CMakeLists.txt 中用 add_compile_definitions(三方库/HAR 场景)
set(WITH_HITRACE_SYSTRACE 1)
add_compile_definitions(WITH_HITRACE_SYSTRACE)
set(WITH_HITRACE_REACT_MARKER 1)
add_compile_definitions(WITH_HITRACE_REACT_MARKER)2.3 抓取 trace
正式采集前,先固定测试条件:
• 使用 release 构建和 release bundle,关闭 Metro、HMR、Inspector 和高频调试日志。
• 固定设备型号、HarmonyOS 版本、RNOH 版本、刷新率策略、网络和测试数据。
• 记录温度、是否充电、电量和电源模式;先预热,再重复相同操作至少 5 次。
• 性能浮层只用于快速观察,正式测量时关闭浮层、录屏和其他可能改变负载的调试功能。
• 同时保留基线数据与问题数据,避免只分析单次偶发 trace。
使用 DevEco Profiler(深度录制)抓取 trace 的详细步骤(操作界面以 DevEco Studio 实际版本为准):
第 1 步:创建分析会话
1. 使用 release 或 debugOptimized 构建模式安装应用,并连接设备;
2. 打开 DevEco Studio → Profiler,在设备列表中选择设备,在进程列表中选择要调测的应用(可以是正在运行的应用,也可以是已安装但未启动的应用);
3. 在新建任务区域选择场景模板,单击 Create Session。常用模板与适用问题:
| 模板 | 适用问题 |
| Frame | 卡顿丢帧:采集 App Frame、RS Frame 和渲染相关数据,适合滑动卡顿类问题 |
| Time | 函数执行效率:深度录制函数调用栈和每帧耗时,展现 ArkTS 到 Native 的跨语言调用栈,适合启动、时延类问题 |
| Launch | 启动耗时:分析启动周期各阶段耗时和核心线程运行情况 |
第 2 步:配置录制选项和泳道
• 录制详情区域的工具栏上有多个配置图标,鼠标悬停可查看功能提示,按需添加录制选项;
• 各泳道区域有下拉选项,可调整该泳道的录制功能;录制前可指定要录制的泳道。
对 RNOH 应用,建议泳道至少包含:**App Frame、RS Frame、应用主线程、RNOH_JS、Render Service**。
第 3 步:启动录制并复现问题
单击任务窗口左上角(或左侧任务列表)的启动按钮,等待任务状态由 "initializing" 变为 "recording" 后,在设备上操作应用、复现性能问题。录制过程中不能再操作其他模板;如需更换模板,需结束本次录制后重新创建会话。
第 4 步:停止录制并等待解析
复现完成后单击停止按钮,泳道任务状态由 "analyzing" 变为 "rendering" 时解析结束,右侧详情区域显示分析结果;数据量较大时解析需要时间,请耐心等待。若 ArkTS Callstack、Callstack 等泳道显示 No Data,可将光标悬浮在泳道名称处的三角告警图标上查看原因。
第 5 步:保存 trace 文件
保存 .htrace 文件,便于后续查询和复核。
抓取完成后,即可在泳道中查看应用主线程、RNOH_JS 等线程的活动区间(同 §1.1 的示例):

更详细的 trace 抓取操作(包括命令行方式、采样配置、trace 文件解析等)请参考 HarmonyOS 官方文档Deep Recording 使用指导,其支持的全部场景模板(Snapshot、Allocation、ArkUI、Energy、ArkWeb、Network、Concurrency、GPU、CPU 等)也以该文档为准。
采集清单:每个问题建议至少提供 — 复现步骤和问题时间点、应用包类型和版本信息、DevEco Profiler trace 文件、App Frame/RS Frame/主线程/RNOH_JS 泳道数据、操作录屏或截图、基线版本与问题版本对照数据。
2.4 查看 trace 的技巧
• 定位 JS 线程:在 trace 中搜索线程名RNOH_JS。
• 定位阶段标记:搜索ReactMarker::,可快速定位到启动、Bundle 加载、Fabric 提交等阶段的起止。
• 定位函数执行:搜索#RNOH::,可定位到具体的函数调用区间(如挂载、触摸分发、TurboModule 调用)。
• 定位异常窗口:先看 App Frame 和 RS Frame 泳道中的红色异常帧,然后缩放到该时间区间,查看该窗口内各线程的活动。
• 确认线程身份:RNOH_MAIN 不在 trace 中直接显示此名称(主线程复用应用自身线程名),需通过应用主线程 + #RNOH::TaskExecutor::runTask等 TraceSection 调用栈确认。
3. RNOH 关键 trace 点解读
3.1 ReactMarker(阶段级异步 trace)
React Marker 的作用是监听 RN 应用生命周期中的关键事件并打点,方便开发者分析性能问题。打点覆盖了从启动到渲染的完整时间线,可归纳为四类:
• 启动初始化:运行时/实例/上下文创建、ArkUI模块注册;
• Bundle 加载:下载、格式转换、分段注册、执行;
• 页面内容显示:内容出现、Surface 前后台切换;
• Fabric 渲染:提交、布局、diff、挂载。
该类 trace 需要开启 WITH_HITRACE_REACT_MARKER 编译开关(见 §2.2)。
查看 ReactMarker 的 trace 时,需要注意标记名与 trace 显示名之间的映射关系:
• 标记大多成对出现,带_START/_STOP(或_END)后缀,例如RUN_JS_BUNDLE_START和RUN_JS_BUNDLE_STOP;
• 成对的两个标记在 trace 中显示为同一个名称,如ReactMarker::RUN_JS_BUNDLE(部分工具显示为H:ReactMarker::RUN_JS_BUNDLE::Tag::<tag>),_START/_STOP后缀不体现在名称中,而是分别对应该异步 trace 时间条的开始和结束;
• 带 tag 的标记会在名称后追加::Tag::<tag>;
• 个别标记名与 trace 显示名不同,如JS_BUNDLE_STRING_CONVERT_START/STOP在 trace 中显示为ReactMarker::loadApplicationScript_StringConvert。
以下是在 trace 工具中查看 ReactMarker 的示例:

图中红色框 1 代表的是应用进程,红色框 2 代表的是应用运行过程中打印出来的 React Marker 信息。
以下按阶段分组列出性能定界中最常用的 ReactMarker。在 trace 工具中搜索 ReactMarker:: 即可找到这些标记。
启动与运行时初始化
| RNOHMarkerId | trace 显示名 | 含义 | 可衡量 | 不可衡量 |
| APP_STARTUP_START / APP_STARTUP_STOP | ReactMarker::APP_STARTUP | RNOH 应用启动总阶段 | 启动阶段的总耗时范围 | 各子阶段细节需看其他 marker |
| INIT_REACT_RUNTIME_START / INIT_REACT_RUNTIME_STOP | ReactMarker::INIT_REACT_RUNTIME | React 运行时初始化 | 运行时创建的起止时间 | JS 引擎是否完全就绪 |
| REACT_INSTANCE_INIT_START / REACT_INSTANCE_INIT_STOP | ReactMarker::REACT_INSTANCE_INIT | RNInstance 实例创建 | 实例创建的起止时间 | 实例创建后是否可立即渲染 |
| CREATE_REACT_CONTEXT_START / CREATE_REACT_CONTEXT_STOP | ReactMarker::CREATE_REACT_CONTEXT | React 上下文创建 | 上下文创建耗时 | Bundle 是否已加载 |
Bundle 加载
| RNOHMarkerId | trace 显示名 | 含义 | 可衡量 | 不可衡量 |
| RUN_JS_BUNDLE_START / RUN_JS_BUNDLE_STOP | ReactMarker::RUN_JS_BUNDLE | JS Bundle 执行阶段 | Bundle 从开始执行到执行完成的时间 | Bundle 下载时间、业务页面首帧时间 |
| JS_BUNDLE_STRING_CONVERT_START / JS_BUNDLE_STRING_CONVERT_STOP | ReactMarker::loadApplicationScript_StringConvert | Bundle 字符串转换 | Bundle 数据格式转换耗时 | Bundle 解析和执行时间 |
| REGISTER_JS_SEGMENT_START / REGISTER_JS_SEGMENT_STOP | ReactMarker::REGISTER_JS_SEGMENT | JS 分段注册 | 多 Bundle 分段注册的耗时 | 分段注册后业务是否就绪 |
| DOWNLOAD_START / DOWNLOAD_END | ReactMarker::DOWNLOAD | JS Bundle 下载阶段 | Bundle 下载起止时间 | 下载后的缓存命中情况 |
| REACT_BRIDGE_LOADING_START / REACT_BRIDGE_LOADING_END | ReactMarker::REACT_BRIDGE_LOADING | React 桥接模块加载阶段 | 整体加载阶段耗时 | 加载内部各步骤的细节 |
Native 模块
| RNOHMarkerId | trace 显示名 | 含义 | 可衡量 | 不可衡量 |
| NATIVE_MODULE_SETUP_START / NATIVE_MODULE_SETUP_STOP | ReactMarker::NATIVE_MODULE_SETUP | ArkUI模块注册和初始化 | 所有ArkUI模块注册的总耗时 | 单个模块的初始化时间 |
| INITIALIZE_MODULE_START / INITIALIZE_MODULE_END | ReactMarker::INITIALIZE_MODULE | 单个模块初始化 | 每个模块的初始化耗时 | 模块初始化后的首次调用时间 |
| PROCESS_PACKAGES_START / PROCESS_PACKAGES_STOP | ReactMarker::PROCESS_PACKAGES | 处理所有 Package | Package 处理的起止时间 | 每个 Package 内部的模块详情 |
| CREATE_UI_MANAGER_MODULE_START / CREATE_UI_MANAGER_MODULE_END | ReactMarker::CREATE_UI_MANAGER_MODULE | 创建 UI_Manager 模块 | UIManager 模块创建的起止时间 | 模块创建后的渲染表现 |
| CREATE_MODULE_START / CREATE_MODULE_END | ReactMarker::CREATE_MODULE | 创建单个 React 模块 | 单个模块创建的耗时 | 模块创建后的调用情况 |
| PROCESS_CORE_REACT_PACKAGE_START / PROCESS_CORE_REACT_PACKAGE_END | ReactMarker::PROCESS_CORE_REACT_PACKAGE | React Native 核心包加载 | 核心包处理的起止时间 | 核心包内部各模块的细节 |
Surface 生命周期
| RNOHMarkerId | trace 显示名 | 含义 | 可衡量 | 不可衡量 |
| CONTENT_APPEARED | ReactMarker::CONTENT_APPEARED | 应用程序中的内容已成功渲染并显示给用户(根节点挂载到 ArkUI 内容容器 NodeContent) | 根节点已挂载的时间点 | 像素已上屏、关键内容可见或可交互 |
| ON_HOST_RESUME_START / ON_HOST_RESUME_END | ReactMarker::ON_HOST_RESUME | Surface 恢复(可见) | Surface 进入可见状态的时间 | Surface 首帧已渲染完成 |
| ON_HOST_PAUSE_START / ON_HOST_PAUSE_END | ReactMarker::ON_HOST_PAUSE | Surface 暂停(不可见) | Surface 进入不可见状态的时间 | Surface 资源已释放 |
Fabric(UI 渲染)
| RNOHMarkerId | trace 显示名 | 含义 | 可衡量 | 不可衡量 |
| FABRIC_COMMIT_START / FABRIC_COMMIT_END | ReactMarker::FABRIC_COMMIT | Fabric 渲染引擎的提交 | React 一次提交的起止时间 | 提交后是否立即触发挂载 |
| FABRIC_DIFF_START / FABRIC_DIFF_END | ReactMarker::FABRIC_DIFF | 新旧影子树对比(diff) | diff 计算的耗时 | diff 结果的 mutation 数量 |
| FABRIC_LAYOUT_START / FABRIC_LAYOUT_END | ReactMarker::FABRIC_LAYOUT | 布局计算(Yoga) | 布局计算的耗时 | 文本测量的内部细节 |
| FABRIC_FINISH_TRANSACTION_START / FABRIC_FINISH_TRANSACTION_END | ReactMarker::FABRIC_FINISH_TRANSACTION | Fabric 事务完成 | 整个事务从提交到完成的耗时 | 挂载是否已在主线程执行完毕 |
| FABRIC_BATCH_EXECUTION_START / FABRIC_BATCH_EXECUTION_END | ReactMarker::FABRIC_BATCH_EXECUTION | mutations 批量挂载执行 | 一批 mutations 在主线程执行的耗时 | 页面内容已完整渲染 |
| FABRIC_UPDATE_UI_MAIN_THREAD_START / FABRIC_UPDATE_UI_MAIN_THREAD_END | ReactMarker::FABRIC_UPDATE_UI_MAIN_THREAD | 主线程同步更新组件属性 | 在主线程上同步更新单个组件属性的执行区间(避免跨线程延迟) | 所有 Fabric transaction 的主线程挂载总时长 |
其他
| RNOHMarkerId | trace 显示名 | 含义 | 可衡量 | 不可衡量 |
| CHANGE_THREAD_PRIORITY | ReactMarker::CHANGE_THREAD_PRIORITY | 线程 QoS 优先级变更 | 线程优先级调整的时间点和目标值 | 优先级变更后的实际调度效果 |
提示:以上仅列出常用标记。完整的 56 个 ReactMarker 标记定义见源码 RNOH/Performance/RNOHMarker.h;其中部分标记(如 INIT_JS_RUNTIME、TURBO_MODULE_SETUP)不会输出到 trace,仅供 Marker 监听器(日志、性能指标等)使用。React Marker 的 trace 打点操作步骤,以及通过自定义 TurboModule 监听 React Marker 事件的实现方式,参见性能调优的 React Marker 介绍。
3.2 TraceSection(函数级同步 trace)
以下列出性能定界中最常用的 TraceSection 标记,在 trace 工具中搜索 #RNOH:: 即可找到。这些标记在 trace 中显示为连续的区间切片,可以直接看到函数的执行时长。
触摸与事件
| trace 字符串 | 含义 | 所属线程 |
| #RNOH::ArkUISurface::onTouchEvent::DOWN | 触摸按下事件进入 RN Surface | RNOH_MAIN |
| #RNOH::ArkUISurface::onTouchEvent::UP | 触摸抬起事件进入 RN Surface | RNOH_MAIN |
| #RNOH::ArkUISurface::onTouchEvent::MOVE | 触摸移动事件进入 RN Surface | RNOH_MAIN |
| #RNOH::Subject::notifyObservers | 事件分发给观察者 | 各线程 |
挂载(Mount)
| trace 字符串 | 含义 | 所属线程 |
| #RNOH::MountingManager::doMount | 开始执行一批 mutations 挂载 | RNOH_MAIN |
| #RNOH::MountingManager::didMount <count> | 挂载完成,count 为 mutation 数量 | RNOH_MAIN |
| #RNOH::MountingManager::dispatchCommand | 分发组件命令(如 scrollTo) | RNOH_MAIN |
Scheduler(Fabric 调度)
| trace 字符串 | 含义 | 所属线程 |
| #RNOH::SchedulerDelegate::performTransaction | 处理一次 Fabric 事务 | RNOH_MAIN |
| #RNOH::SchedulerDelegate::onNextFrame | 下一帧调度 | RNOH_MAIN |
| #RNOH::SchedulerDelegate::didMount | Scheduler 挂载完成回调 | RNOH_MAIN |
| #RNOH::SchedulerDelegate::handleMutation_parallel | 并行处理 mutations | RNOH_MAIN |
| #RNOH::SchedulerDelegate::parallelCreateWait | 并行创建组件等待 | RNOH_MAIN |
| #RNOH::SchedulerDelegate::otherCreate | 串行创建组件 | RNOH_MAIN |
TaskExecutor(任务调度)
| trace 字符串 | 含义 | 所属线程 |
| #RNOH::TaskExecutor::runTask | TaskExecutor 执行一个任务 | 各线程 |
| #RNOH::TaskExecutor::runningTask | 正在执行的任务(含任务 ID) | 各线程 |
| #RNOH::TaskRunner::task | 事件循环执行单个任务 | RNOH_MAIN / RNOH_JS |
实例与通信
| trace 字符串 | 含义 | 所属线程 |
| #RNOH::RNInstanceCAPI::synchronouslyUpdateViewOnUIThread | 主线程同步更新组件属性(避免跨线程延迟) | RNOH_MAIN |
| #RNOH::MessageQueueThread::runOnQueue | 向 JS 队列投递任务 | RNOH_JS |
| #RNOH::RNInstanceInternal::callJSFunction | 调用 JS 函数 | RNOH_JS |
| #RNOH::ArkTSTurboModule::call (<name>::<methodName>) | ArkTS TurboModule 调用(含模块名和方法名) | RNOH_MAIN |
| #RNOH::TextMeasurer::measure | 文本测量 | RNOH_JS |
提示:以上仅列出常用标记。完整的 TraceSection 字符串可通过在源码中搜索 TraceSection s( 或 SystraceSection s( 获取。
3.3 按 trace 时间轴分析一次点击响应
单个 trace 切片只能说明某个环节的执行区间,定位性能问题时需要按时间顺序将多个切片串联,还原从输入到上屏的完整执行过程。
以一次点击响应为例,完整的 trace 点序列如下(两条竖线代表两个线程,从上到下即时间顺序):

依据各切片之间的时间间隔,可以定位耗时集中在哪个环节:
• 如果onTouchEvent::DOWN到runOnQueue之间很长 → 主线程调度或触摸分发慢;
• 如果FABRIC_COMMIT到FABRIC_FINISH_TRANSACTION之间很长 → JS 侧 React render/layout/diff 慢;
• 如果FABRIC_BATCH_EXECUTION到didMount之间很长 → 主线程挂载慢;
• 如果didMount到首个响应帧上屏之间很长 → ArkUI 布局/绘制或 Render Service 合成慢。
4. 定界方法与常见错误
4.1 责任域划分
一次有效定界至少给出以下结论之一:
| 责任域 | 典型证据 | 后续处理方 |
| HarmonyOS 系统或 Render Service | RS Frame 异常,应用按时提交;同设备多个应用同时劣化 | 系统/图形侧 |
| ArkUI宿主或 ArkTS 业务 | 应用主线程被非 RNOH 的 ArkTS、I/O、图片处理等任务占满 | ArkUI业务侧 |
| RN JS 业务 | RNOH_JS 长任务、频繁 React 提交或事件回调过重 | RN 业务侧 |
| 三方 RN 组件 | 异常调用栈集中在三方组件,移除或降级后恢复 | 三方组件维护方 |
| RNOH 框架 | 异常窗口关键路径落在 RNOH 实现,最小示例仍复现,且版本对比显示框架回归 | RNOH 框架侧 |
| 证据不足 | 只有主观现象、平均 FPS 或单个长切片,无法覆盖从输入到上屏的完整过程 | 补充采集 |
"使用了 RNOH"不等于"RNOH 框架问题"。例如,大量业务组件一次性提交后,挂载耗时发生在 RNOH 主线程,但如果耗时随业务节点数增长且最小 RNOH 页面正常,通常应先归为 RN 业务负载或组件使用问题,而不是框架缺陷。
4.2 定界前准备
• 确认 trace 编译开关已开启:检查WITH_HITRACE_REACT_MARKER和WITH_HITRACE_SYSTRACE是否在构建配置中设置为 ON(详见 §2.2)。
• 测试条件的固定(release 构建、设备/版本/数据固定、预热重复、基线数据与问题数据等)见 §2.3 开头的采集要求,此处不再重复。
4.3 三类问题的快速判定
滑动卡顿丢帧

详细方法见RN性能调优之滑动卡顿问题介绍。
响应时延
按"输入锚点 → 首个响应帧上屏"分段测量,定位耗时最长的一段:
输入 → 应用触摸入口 慢 → 系统输入分发 / 主线程调度
触摸分发完成 → JS 事件 晚 → RNOH_JS 排队或阻塞
JS 提交 → 首个变化上屏 晚 → 挂载、ArkUI 或 RS
全程无任何视觉变化 → 先检查业务是否设计了即时反馈完成时延
先定义"完成"终点,再分段定位责任方:
数据/网络未就绪 → 业务或服务侧
JS render/commit 晚 → RN JS 业务或三方组件
mutations 已产生但挂载晚 → RNOH_MAIN、节点规模或同步阻塞
应用按时提交但上屏晚 → ArkUI/RS/系统侧
仅 CONTENT_APPEARED 早 → 不能据此判定页面已经完成详细方法见RN性能调优之点击响应和完成时延分析介绍。
4.4 常见错误定界
• 只看 JS FPS/UI FPS 就分责:浮层是采样指标,适合发现趋势,不足以证明一次异常的关键路径。
• 只看一个超过帧预算的函数:它可能不在异常帧、可能运行在非关键线程,也可能被后续流水线吸收。
• 应用和 RS 同时红就判系统问题:应用晚提交也会向 RS 传导,必须看最早异常点。
• RNOH 调用栈长就判框架问题:需要结合输入规模、最小示例和版本对照。
• **把 CONTENT_APPEARED 当完成时延终点**:该标记早于实际上屏和业务内容完整。
• 用 debug 数据和 release 基线比较:构建、日志、Inspector 和 bundle 差异会破坏对照条件。
• 把平均值当全部结论:平均 FPS 会掩盖最大连续丢帧,平均时延会掩盖 P95/P99 尖峰。
相关文档
• RN 性能调优之点击响应和完成时延分析介绍(系列第二篇)
----------------------------------------------------------------------------------------------------------

🔗 社区DFX专题文章: https://developer.huawei.com/consumer/cn/forum/subject/2101218731402391001?ha_source=zxqy-IT&ha_sourceId=89000468


【扫码加入 HarmonyOS DFX 技术交流群】
相关推荐
鸿蒙小助手
22
0
鸿蒙小助手
30
0
鸿蒙小助手
16
0
鸿蒙小助手
25
0
鸿蒙小助手
35
0
鸿蒙小助手
致力于为鸿蒙开发者谋福利
帖子
提问
粉丝
RN 性能调优之滑动卡顿问题介绍
2026-10-21 14:43:44 发布RN 性能调优之点击响应和完成时延分析介绍
2026-10-21 14:33:16 发布



京公网安备:11010502051901号