鸿蒙小助手 2026-10-21 14:33:16 发布本原创文章帖发布在华为开发者联盟社区,欢迎开发者前往访问评论交流,更多与该内容相关讨论,请点击原帖查看:
RN 性能调优之点击响应和完成时延分析介绍-华为开发者话题 | 华为开发者联盟
响应时延是指用户输入触控指令后,应用给出首个可见反馈所用的时间;完成时延是指用户输入触控指令后,界面完全刷新并达到可阅读的稳定状态所用的时间。完成时延包含响应时延,两者共同决定用户对操作流畅性的感知。
当应用出现明显的点击无反馈、页面加载迟缓等现象时,需要分析时延链路中各阶段的耗时,并针对性优化。RN应用的页面结构、bundle加载方式与ArkTS应用差异较大,难以直接套用系统时延基线,建议以同场景的基线数据(如历史版本测量值或业务目标值)为参照判断时延是否超标。
本文是RNOH性能定位系列的第二篇,说明如何在RNOH应用中定位响应时延与完成时延问题,重点介绍业务侧问题的定位与优化;定界方法可将问题归入业务侧、RNOH框架侧或系统侧(划分标准见§5.3)。线程模型、trace采集方法、trace标记含义等基础内容见RN性能调优之基础定界与trace分析介绍;滑动过程的连续丢帧问题见RN性能调优之滑动卡顿问题介绍。
1. 指标定义
1.1 响应时延与完成时延
响应时延 = 首个响应帧上屏时间 − 输入起点完成时延 = 达到约定完成状态的时间 − 操作起点

两项指标相互独立,应分别测量:
• 响应时延对应交互反馈的即时性,典型反馈包括按钮按压态、页面开始移动、首个新内容上屏;
• 完成时延对应操作的完成程度,完成状态须在测试前约定,例如列表占位全部替换、转场动画结束或关键内容可见;
• 同一次点击的响应时延达标而完成时延超标是常见现象,两项指标应分别优化、分别评估。
1.2 明确测量的起止点
时延数值取决于起点和终点的选取方式。测量前必须写清以下四项,优化前后的对比必须使用同一组起止点:
• 输入起点:取物理触屏时刻、系统输入事件上报时刻还是业务打点时刻,不同工具的默认值不同;
• 响应终点:对应哪个可见变化(按压态、页面开始移动、首个新内容上屏);
• 完成终点:对应系统帧、动画结束还是关键业务内容可见;
• 工具与版本:记录所用工具名称、版本和已知限制。
HarmonyOS各性能工具对起止点的默认取法和已知限制,请参考性能分析与HiSmartPerf Device 性能使用指导。
1.3 RNOH内部启动指标及其边界
RNOH提供以下内部指标,用于分解启动阶段耗时:
| 指标 | 定义 | 可说明什么 | 不能说明什么 |
| CONTENT_APPEARED | 根ArkUI节点加入 NodeContent的时刻 | 页面框架已挂载的时间点 | 像素已上屏、数据图片等内容已完整、页面可交互 |
| ROOT_VIEW_TTI | APP_STARTUP_START到 CONTENT_APPEARED的汇总耗时 | RNOH内部启动阶段的分解 | 通用意义上的"页面可交互时间" |
| APP_STARTUP | APP_STARTUP_START/STOP的汇总耗时 | RNOH记录的启动范围 | 应用进程完整启动耗时 |
这些指标适合分解RNOH启动阶段,但不能直接等同于完成时延:CONTENT_APPEARED早于实际像素上屏,也可能早于数据、图片和关键业务内容完成。报告中应注明指标的真实起止点,避免仅按名称推断。
2. 端到端时延链路
2.1 响应时延
从用户点击到首个响应帧上屏,依次经过系统输入、事件分发、JS处理、React提交、ArkUI组件挂载和上屏,共6个时间锚点(ts0~ts5,锚点用于在系统、主线程、JS线程等不同泳道的trace上对齐同一时刻):

各锚点在trace中的对应位置:
| 锚点 | 对应trace标记 | 所在线程 |
| ts0系统输入 | H:service report(type为up)等系统输入事件 | 系统输入服务 |
| ts1 Surface收到触摸 | #RNOH::ArkUISurface::onTouchEvent::DOWN | RNOH_MAIN |
| ts2 JS handler开始 | 业务自定义trace点 | RNOH_JS |
| ts3 React提交 | ReactMarker::FABRIC_COMMIT_START | RNOH_JS |
| ts4挂载完成 | ReactMarker::FABRIC_BATCH_EXECUTION_END、didMount <count> | RNOH_MAIN |
| ts5首个响应帧上屏 | App Frame首个变化帧 | Render Service |
并非每次响应都经过完整链路。ArkUI系统级按压态、系统级滚动可以在 JS 更新前产生反馈。分析时应从实际的首个可见变化反向定位其产生环节。
2.2 完成时延
完成时延覆盖页面加载/切换从操作开始到业务完成状态的全过程:

下图为一次真实点击跳转(1500个Text组件页面)的渲染三阶段trace,对应链路图中"容器创建 → 组件构建 → 挂载 → 上屏"各阶段的实际耗时分布:

两个要点:
• 数据准备可与组件构建并行:页面框架渲染不必等待全部数据返回,但关键内容渲染必须等数据到位。不在端到端关键路径上的并行工作,其耗时不计入完成时延;
• 完成时延可能包含网络和业务异步阶段:不能将整个区间都归因于渲染框架,应为数据请求开始/结束、JS render、挂载和业务完成分别打点。
3. 问题定位流程
3.1 定位流程
定位响应时延与完成时延问题通常需要以下步骤:

1. 确认时延异常:使用DevEco Profiler采集系统帧泳道,对照同场景基线数据(历史版本测量值或业务目标值)确认时延确实超标,排除测试条件不稳定因素;
2. 明确测量起止点:按§1.2约定输入起点、响应终点和完成终点;
3. 采集trace:使用release构建复现问题,采集App Frame、RS Frame、应用主线程、RNOH_JS泳道数据(§4.1);
4. 分段定位:建立统一时间轴(§3.3),通过基线数据与问题数据的对比,识别首个显著变长的区段(§3.4);
5. 沿责任域深入分析:结合关键泳道定位耗时函数与组件(§4.4),按区段责任域展开分析(§5);
6. 实施优化并复测:参考§6优化案例实施处理,优化后重复测量确认达标;未达标则重新分段定位。
3.2 定位前准备
• 使用release构建和release bundle,关闭Metro、HMR、Inspector、高频日志,正式测量时关闭性能浮层;
• 固定设备、HarmonyOS/RNOH版本、刷新率策略、网络、账号、缓存和测试数据;
• 冷启动(进程首次创建)与热启动(进程复用)、首次进入页面与再次进入页面必须分别统计;
• 每种操作重复至少5次,报告中至少给出中位数和最差值,批量测试建议增加P90/P95分位值(90%/95% 的数据优于此值);
• 为操作开始、请求完成、关键数据到达、动画结束和关键内容可见增加业务trace点。
性能浮层的JS FPS/UI FPS是500ms轮询的趋势指标,适合观察持续负载,不适合定位一次几十毫秒量级的点击响应问题。
3.3 建立统一时间轴
报告中同时保留绝对时间戳和相对操作起点的毫秒数:
| 时间点 | 绝对时间戳 | 相对起点 | 来源 |
| 输入起点 | ts0 | 0 ms | 系统输入 marker |
| RN Surface 收到 DOWN | ts1 | +x ms | RNOH trace |
| JS handler 开始 | ts2 | +x ms | 业务/JS trace |
| Fabric commit | ts3 | +x ms | RNOH marker |
| native batch 结束 | ts4 | +x ms | RNOH marker |
| 首个响应帧上屏 | ts5 | +x ms | 系统帧 |
| 业务完成 | ts6 | +x ms | 系统/业务 marker |
如果trace中没有JS handler或业务完成点,应先补充打点,不要用名称相似但语义不同的切片代替。
3.4 分段定位
将问题数据与基线数据按同一组锚点切段、逐段比较时长,首个显著变长的区段通常比单个最长切片更接近根因:

4. 使用 Profiler 工具检测和分析
DevEco Profiler 是 DevEco Studio 提供的场景化调优工具,其中 Frame 模板可以录制应用运行过程中的关键数据,用于深度分析卡顿丢帧、耗时长等问题。本节说明在时延分析场景下的使用方法,更通用的操作说明可参考Frame 分析与官方最佳实践《点击响应时延分析》、《点击完成时延分析》。
4.1 抓取操作 trace
1. 使用 release 或 debugOptimized 构建模式安装应用,连接设备;
2. 打开 DevEco Studio → Profiler,选择设备与目标应用、进程,选择 Frame 模板,单击 Create Session 创建会话;
3. 确认 RNOH trace 编译开关已开启(WITH_HITRACE_REACT_MARKER、WITH_HITRACE_SYSTRACE,配置方法见基础篇「2.2 开启 trace 编译开关」一节),否则 trace 中不会出现 ReactMarker::* 与 #RNOH::* 标记;
4. 单击启动按钮开始录制,在设备上执行待分析操作,完成后停止录制并保存 trace 文件。
泳道选择与录制配置的详细说明,见基础篇《RN性能调优之基础定界与 trace 分析介绍》的「2.3 抓取 trace」一节。
4.2 确定时延起点和终点
响应时延:
• 起点:手势抬起的那一帧,对应 mmi-service 泳道中H:service report的 type 为 up 的事件;输入分发到达应用后,可在应用侧H:DispatchTouchEvent(type=1)处对齐(起点到该事件之间为输入阶段耗时,见 §4.3);
• 终点:页面变化后的第一帧,对应 RSHardwareThread 泳道CommitAndReleaseLayers的结束点。
完成时延:
• 起点:与响应时延一致(点击离手);
• 终点:trace 中无统一的明确标记,需借助录屏分析工具确定完成耗时,再从起点向后定位对应时间点;建议业务侧为完成状态(如列表占位全部替换)增加自定义 trace 点。
4.3 分解时间段
官方将点击响应拆分为输入、应用、渲染三个阶段(见《点击响应时延分析》)。RNOH 应用的"应用阶段"涉及主线程与 JS 线程协作,可进一步细分:
| 阶段 | 起点 | 终点 | 说明 |
| 输入阶段 | H:service report(type=up) | 应用 H:DispatchTouchEvent 起点 | 系统输入分发 |
| 事件分发 | H:DispatchTouchEvent | #RNOH::ArkUISurface::onTouchEvent::DOWN 结束 | 主线程触摸分发与命中测试 |
| JS 处理 | 事件投递至 JS 线程(runOnQueue) | FABRIC_LAYOUT / FABRIC_DIFF 结束 | JS handler 与 React 提交 |
| ArkUI挂载 | FABRIC_FINISH_TRANSACTION 起点 | didMount <count> 结束 | mutations 挂载 |
| 渲染阶段 | RS 帧 ProcessCommandUni 起点 | RSHardwareThread CommitAndReleaseLayers 结束点 | 系统合成 |
官方文档中的阶段基线面向纯 ArkUI 应用;RNOH 应用包含跨线程 JS 处理环节,评估各区段耗时应以同场景的基线数据为参照。
4.4 分析定位原因
针对待分析的时延区间,结合关键泳道定位具体问题:
• Frame 泳道:观察应用主线程帧数据,红色帧表示该帧渲染时间超出预期。页面跳转后的首帧渲染时间往往较长,首帧出现红色属常见现象,不一定表示严重性能问题,但仍需关注;
• RNOH_JS 线程泳道:长段ExecuteJs说明存在 JS 长任务,展开调用栈或火焰图定位耗时函数;
• ArkTSCallstack / Callstack泳道:分别提供 ArkTS 侧与 Native 侧方法调用栈,用于定位高耗时函数;
• ArkUI Component 泳道:记录组件创建、布局、渲染耗时,识别耗时明显偏长的组件;
• H:Animator 泳道:观察转场动画耗时,动画时长设置过长会直接增大完成时延。
各区段的责任域划分与深入分析方法见 §5。
5. 分段定位详解
本节将 §3.4 识别出的异常区段落到具体责任域。分段定位的结论应归入业务侧、RNOH 框架侧或系统侧之一,划分标准见 §5.3。
5.1 响应时延各区段
分析前置:先确认是否缺少即时反馈
若点击后页面没有任何视觉变化,应先补充即时反馈再深入分析:按压态、loading、骨架屏等轻量反馈位于关键路径前部,是改善响应时延最直接的手段(见案例一)。即时反馈不能替代根因定位,处理耗时过长仍需按以下区段继续分析。
ts0 → ts1:系统输入到 RN Surface
RNOH 在 ArkUISurface.cpp 中记录 #RNOH::ArkUISurface::onTouchEvent::DOWN/MOVE/UP/CANCEL。
该区段耗时异常,通常指向系统输入分发、应用主线程调度或前序任务阻塞。此时事件尚未进入 RNOH 触摸分发,不应归因于 JS 侧。
ts1 → ts2:事件交接与 JS 排队
主线程完成触点命中测试(确定触摸落在哪个组件上)、坐标变换和事件发射,事件随后投递到 JS 线程。复杂触摸树和变换可能增加此段耗时,但应先在调用栈中证实该区段确实偏长,再做结构优化。
该区段耗时异常,通常指向主线程被 ArkTS 业务、I/O、同步任务阻塞,或 JS 线程被长计算、GC、同步 TurboModule 占用导致事件排队。若 RNOH_JS 长时间处于 Runnable(就绪但未获得 CPU)而非 Running(执行中),应检查 CPU 竞争、QoS 和温控,而非优化 JS 代码。
ts2 → ts3:业务处理与 React 提交
JS handler 执行业务逻辑并触发 React 更新。建议为目标 JS handler 增加自定义 trace,检查:
• handler 是否同步解析大数据、执行循环或触发大量状态更新;
• handler 结束后 Fabric commit(FABRIC_COMMIT_START)是否及时开始;
• 关闭 handler 中某段业务后,响应帧是否相应提前。
ts3 → ts4:Fabric 处理与ArkUI挂载
重点关注 FABRIC_DIFF_*、FABRIC_LAYOUT_*、#RNOH::SchedulerDelegate::performTransaction、#RNOH::MountingManager::doMount、didMount <count>、FABRIC_BATCH_EXECUTION_*,以及 ArkUI 的 measure/layout/flush 切片。
注意:FABRIC_UPDATE_UI_MAIN_THREAD_* 仅表示 synchronouslyUpdateViewOnUIThread 同步更新单个组件属性的区间,不能将 FABRIC_COMMIT_START → FABRIC_UPDATE_UI_MAIN_THREAD_END 当作通用挂载完成时延。
ts4 → ts5:上屏
native batch 已及时结束但响应帧仍延迟上屏时,依次检查:
1. App Frame 是否已异常;
2. 应用主线程的 ArkUI measure/layout/flush;
3. RS Frame 与应用提交时间的先后;
4. GPU、图片上传、复杂图层和系统资源竞争。
App Frame 正常而 RS Frame 异常时,优先定界到系统合成侧;两侧均异常时,通过时间顺序判断是应用晚提交向后传导,还是合成阻塞反向施加压力。
5.2 完成时延关键阶段
| 阶段 | 主要观察点 | 常见责任域 |
| 操作到路由开始 | JS handler、ArkUI路由调度 | RN/ArkTS 业务 |
| 容器创建 | RN Surface、ArkUI页面和组件初始化 | RNOH/ArkUI业务 |
| 数据准备 | 网络、数据库、缓存、序列化 | 业务/服务侧 |
| React 产出 | render、commit、diff、layout | RN 业务/三方组件 |
| native 挂载 | mutation 数量、组件创建、属性更新 | RNOH/组件实现/业务规模 |
| ArkUI/RS 上屏 | App Frame、RS Frame、flush/合成 | ArkUI/系统/资源 |
| 关键内容完成 | 图片、动画、二次请求、占位替换 | 业务侧 |
判定时注意两点:
• 只有位于端到端关键路径并推迟完成终点的阶段才计入根因。后台并行工作即使耗时较长,也可能不影响用户看到的完成状态;
• 若页面框架先行渲染、文本和图片随后补齐,应分别记录:请求发出与返回、数据转换与 JS render、图片下载/解码/上传、最终内容上屏。文本和图片均延迟,优先检查数据绑定或 transaction 时机;文本已显示而图片延迟,优先检查图片链路。
RNOH 首次加载相关的启动 marker(RUN_JS_BUNDLE_*、TURBO_MODULE_SETUP_*、CONTENT_APPEARED 等)见基础篇「3.1 ReactMarker(阶段级异步 trace)」一节。
5.3 责任域划分
分段定位的结论应归入以下三类责任域之一:
| 责任域 | 典型证据 | 定界结论 |
| 业务侧 | JS handler 长任务;React 重渲染或提交过频;列表虚拟化配置不当;图片规格过大;转场动画时长过长;网络请求发起时机滞后;缺少即时反馈;bundle 打包方式(如未使用 HBC) | 业务负载或使用方式问题,可按 §6 优化案例直接处理,处理后复测验证 |
| RNOH 框架侧 | 异常窗口关键路径落在 RNOH 实现;移除业务负载后的最小 RNOH 示例仍能复现;同负载下 RNOH 版本 A/B 显示明确回归 | 定性为 RNOH 框架问题;整理 trace、最小示例与版本对照等定界材料,需要时可向 RNOH 社区反馈 |
| 系统/图形侧 | App Frame 正常而 RS Frame 单独异常;线程运行核、频率等系统调度异常 | 定性为系统/图形侧问题;结合系统分析工具继续排查,定界材料同上保留 |
两点补充说明:
• 耗时落在 RNOH 实现上不等于框架问题。doMount、performTransaction等切片耗时长时,先做负载缩放(节点数降为 1/2、1/4)和最小示例对照:耗时随业务规模近似等比变化的,通常仍是业务负载问题(组件数量、层级导致),应归入业务侧;
• 三方 RN 组件异常调用栈集中在三方组件、替换或降级后恢复的,定界为三方组件问题。
6. 优化案例
本节针对响应时延与完成时延的常见问题提供三个优化案例:JS 长任务、loadBundle 耗时长,以及通过 Hermes 字节码(HBC)优化 bundle 加载。每个案例按"问题现象 → 定位分析 → 优化措施 → 效果验证"组织。优化后应复测确认时延达标,未达标则回到 §3.1 重新分段定位。更多场景的优化手段可参考页面转场与滑动场景优化与性能调优。
6.1 案例一:JS 长任务导致点击响应时延过长
问题现象:点击列表项跳转详情页,点击后长时间无按压反馈。采集数据显示响应时延约 240ms,同场景基线数据约 72ms。
定位分析:
1. 按 §3.3 建立统一时间轴:输入后 +18ms 事件已进入 ArkUISurface,系统输入分发正常;RNOH_JS 到 +173ms 才开始执行目标 handler,此前被同步数据转换占用;
2. handler 结束后 +14ms 内出现 Fabric commit,native batch 未超出帧预算,首个响应帧在 JS 提交后正常上屏,RS Frame 正常;
3. 分段定位结论:ts1→ts2(事件交接与 JS 排队)为首个显著变长的区段,根因为 JS 线程被同步数据转换阻塞。

优化措施:
• 将点击 handler 中的同步数据转换移出关键路径,改为异步处理或延后到页面挂载后执行;
• 为点击入口补充即时反馈,按场景组合使用:列表项按压态(组件自带,成本最低)、loading(适用于等待时长较短或不确定的场景)、骨架屏(适用于页面结构可预期的加载场景)。响应时延以首个可见反馈为终点,补充即时反馈可以避免处理耗时的波动直接影响用户感知。
效果验证:优化后响应时延降至 78ms;通过关闭同步数据转换前后的对照数据确认了因果关系。
6.2 案例二:loadBundle耗时长导致完成时延长
问题现象:首次点击进入 RN 页面后,页面长时间停留在过渡状态,完成时延约 1269ms;再次进入明显更快,说明耗时集中在首次加载环节。
定位分析:
1. 在 trace 中搜索 loadBundle 定位 bundle 加载执行区间(RNOH_JS 线程上的 Instance::loadBundle 区段):本例约 739ms,位于点击后的关键路径上,与容器创建、首屏渲染串行执行。如需进一步分解:bundle 下载耗时查看 ReactMarker::DOWNLOAD,bundle 执行耗时查看 ReactMarker::RUN_JS_BUNDLE;
2. bundle 执行结束后各阶段耗时正常(首屏渲染 330ms、上屏 120ms),排除渲染负载问题;
3. 结论:完成时延过长的主因是 bundle 在点击后才串行加载,首次加载的耗时会全部计入完成时延。
真实 trace 中 loadBundle 耗时长的问题形态(RNOH_JS 线程上 bundle 加载与执行区间,本例 739ms):

上图的分段耗时示意:

优化措施:
• 预创建 RN 实例:对于使用独立 Bundle 的业务模块,在应用启动阶段提前初始化RNInstance并加载对应 bundle,用户点击时实例与 bundle 均已就绪;
• FrameNode预加载 RN 页面:利用FrameNode与NodeContainer,在后台提前构建目标页面的ArkUI节点树,跳转时直接挂载显示;
• 预加载时机选择应用启动阶段或空闲时段。bundle 加载耗时并未消失,只是移出了点击后的关键路径。
效果验证:优化后点击进入的完成时延降至约 530ms(loadBundle 的 739ms 移出关键路径);trace 中 loadBundle 区间出现在启动阶段而非点击后的时间窗口。配合案例三的 HBC 可进一步缩短 bundle 执行本身的耗时。
6.3 案例三:使用 Hermes 字节码(HBC)优化 bundle 加载
问题现象:应用首次加载慢,trace 中 ReactMarker::RUN_JS_BUNDLE 区段(bundle 执行)耗时长;使用文本 bundle 时还存在 loadApplicationScript_StringConvert(Bundle 字符串转换)开销。
原因分析:默认打包产物为 JavaScript 文本,Hermes 加载时需要先做字符串转换,再解析、编译为字节码后才能执行,这些工作全部位于启动关键路径上。Hermes 支持在构建阶段通过 hermesc 工具将 JavaScript 预编译为字节码(HBC),应用运行时直接执行字节码:跳过解析编译步骤,缩短 bundle 执行时间;同时字节码体积更小、内存占用更低。

操作方法:打包时增加 hermesc 编译步骤,将 bundle 转为 HBC 文件:
npm run codegen && react-native bundle-harmony --dev=false --minify=true --bundle-output /tmp/bundle.harmony.js && hermesc --emit-binary /tmp/bundle.harmony.js -out ./harmony/entry/src/main/resources/rawfile/hermes_bundle.hbc效果验证:对比优化前后 RUN_JS_BUNDLE_START 到 RUN_JS_BUNDLE_STOP 的耗时变化;文本 bundle 的字符串转换开销也随之消除。
相关文档
• RN性能调优之基础定界与 trace 分析介绍(基础篇:线程模型、trace 采集、标记含义)

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


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



京公网安备:11010502051901号