鸿蒙中Frame分析
在应用开发中,流畅的用户体验至关重要,而帧率(FPS)是衡量应用流畅度的关键指标之一。无论是前端Web开发还是鸿蒙应用开发,Frame分析(帧率分析)都是优化性能、解决卡顿丢帧问题的核心手段
1 Frame分析概述:为什么它如此重要
Frame分析是指通过监测和记录应用渲染过程中的每一帧,分析其耗时、找出卡顿原因并进行优化的过程。在前端开发中,我们通常关注浏览器渲染性能;而在鸿蒙应用开发中,则需关注ArkUI框架的整体渲染流水线。
理想的帧率通常为60FPS,这意味着每帧的渲染时间不得超过约16.6ms(1/60秒)。如果某些帧的渲染时间超过这个阈值,就会出现丢帧(Jank),用户则会感受到界面卡顿
2 前端开发中的Frame分析
前端开发中,Frame分析通常依赖于浏览器提供的性能工具。
2.1 常用工具与方法
-
Chrome DevTools Performance面板:前端开发者最常用的工具之一。它可以录制一段时间内的JavaScript执行、渲染和绘制过程,并通过火焰图(Flame Chart) 展示每一帧的详细耗时。
-
FPS计数器:实时监控页面的帧率变化,快速定位帧率下降的场景。
-
Frame Timing API:浏览器提供的API,允许开发者通过代码获取帧耗时数据,实现自定义的性能监控。
2.2 前端常见卡顿场景与优化策略
-
强制同步布局(Forced Synchronous Layout):在JavaScript中频繁读取或修改DOM样式,导致浏览器为了计算布局而中断当前任务,进而引发性能问题。优化策略:避免在循环中操作DOM,使用
requestAnimationFrame
进行动画更新。 -
长时间运行的JavaScript任务:复杂的运算或执行时间过长的函数会阻塞主线程,延迟渲染。优化策略:将任务Web Worker拆分,或使用异步操作避免阻塞。
-
样式复杂性与重绘重排:过于复杂的CSS选择器、频繁的重排(Reflow)与重绘(Repaint)都非常消耗资源。优化策略:简化选择器,使用
transform
和opacity
属性实现动画(这些属性不会触发重排
3 鸿蒙开发中的Frame分析
鸿蒙应用开发使用方舟UI框架(ArkUI),其Frame分析工具集成在DevEco Studio中,功能强大且针对系统特性进行了深度定制。
3.1 DevEco Studio Frame Profiler
DevEco Studio内置了Profiler分析调优工具,其中的Frame分析模块用于录制GPU数据信息,深度分析应用或服务卡顿丢帧的原因
3.1.1 核心功能
功能模块 | 描述 | 用途 |
---|---|---|
App Frame泳道 | 显示应用侧每一帧的渲染数据。绿色为正常帧,红色为卡顿帧。 | 定界问题发生在应用侧还是渲染服务侧 |
RS Frame泳道 | 显示Render Service(渲染服务)侧的帧数据。同样用颜色标识正常与卡顿帧。 | |
ArkTS Callstack | 展示UI主线程的方法栈调用情况 | 定位导致卡顿的具体代码块 |
CPU Core | 显示录制过程中的CPU核心占用情况 | 辅助判断是否因CPU资源不足导致卡顿 |
3.12 使用步骤
-
打开Profiler:在DevEco Studio中选择
View -> Tool Windows -> Profiler
。 -
创建会话:点击"Create Session",选择待分析的设备与应用进程。
-
录制数据:点击开始录制,在设备上复现卡顿操作后停止录制。
-
分析数据:观察Frame泳道,定位红色卡顿帧,并结合ArkTS Callstack分析具体原因
3.2 鸿蒙常见卡顿场景与优化策略
3.2.1 长列表滑动卡顿
问题代码:
// 优化前:未复用的列表项与深拷贝
@Component
struct ArticleCardView {@Prop item: ArticleData; // @Prop对复杂对象深拷贝,损耗性能build() {Row() {// 复杂嵌套布局}}
}
优化策略:
-
组件复用:使用
@Reusable
装饰器缓存组件,通过aboutToReuse
方法更新内容,减少组件重建开销。 -
避免深拷贝:用
@Builder
构建轻量子组件替代复杂的@Component
组件,避免@Prop
深拷贝带来的性能损耗。 -
懒加载优化:为
LazyForEach
设置合理的cachedCount
参数
优化后代码:
@Reusable // 启用组件复用
@Component
struct ArticleCardView {aboutToReuse(params: Record<string, Object>) { // 复用回调this.item = params.item as ArticleData;}build() {Row() {ActionButtonBuilder({ icon: this.item.icon }) // 改用Builder避免深拷贝}}
}// 用Builder替代@Component组件
@Builder
function ActionButtonBuilder(icon: Resource) {Button(icon).width(40).height(40)
}
3.2.2 自定义动画丢帧
问题代码:
// 错误示范:在主线程循环计算阻塞渲染
computeSize() {for (let i = 1; i <= doTimes; i++) {setTimeout(() => {this.heightSize += deltaHeight;this.widthSize += deltaWeight; // 主线程频繁计算}, i * period);}
}
优化策略:
改用系统动画API,让GPU自动插值计算,解放主线程
Button('click me').onClick(() => {this.widthSize = this.flag ? 100 : 200;this.heightSize = this.flag ? 50 : 100;this.flag = !this.flag;}).animation({ // 使用系统属性动画duration: 2000,curve: Curve.Linear,delay: 500})
效果:帧率从63fps提升至116.9fps(设备支持120Hz)
3.2.3 布局嵌套过深
问题现象:列表项嵌套20层Stack,Measure布局耗时超标。
分析工具:ArkUI Inspector(可视化查看组件树,定位冗余嵌套)。
优化方案:
-
删除无意义嵌套,用
RelativeContainer
(相对布局)替代多层Stack
。 -
精简合并组件样式。
3.2.4 主线程耗时操作
高频踩坑6:
-
在
onClick
中同步读取大文件。 -
列表滚动时实时计算数据。
优化技巧
// 错误!主线程同步IO阻塞渲染
onClick(() => {let data = fs.readFileSync('huge_data.json');
})// 正确方案:耗时操作交给Worker线程
onClick(() => {const worker = new worker.ThreadWorker('workers/io.js');worker.postMessage('huge_data.json');
})
关键原则:主线程只负责UI更新和手势响应,将IO、计算等耗时任务交给Worker线程或异步队列
4 前端与鸿蒙Frame分析对比
尽管目标一致,但前端和鸿蒙在Frame分析的工具和细节上存在差异。
分析维度 | 前端开发 | 鸿蒙应用开发 |
---|---|---|
主要工具 | Chrome DevTools Performance面板 | DevEco Studio Frame Profiler |
核心指标 | FPS、帧耗时、布局、重绘、复合时间 | FPS、App帧耗时、RS帧耗时 |
卡顿定义 | 帧耗时超过16.6ms(60FPS) | 帧的实际结束时间晚于期望结束时间 |
优势 | 生态成熟,与浏览器深度集成 | 系统级集成,能定位到更底层的渲染服务问题 |
局限/差异 | 受限于浏览器环境,无法获取系统底层数据 | 需要USB连接真机,仅支持较新的API版本 |
5 通用优化技巧与最佳实践
无论前端还是鸿蒙开发,以下建议都有助于提升帧率:
-
减少不必要的布局嵌套:精简组件树结构是永恒的优化主题。
-
善用硬件加速:前端中使用
transform
和opacity
属性;鸿蒙中合理使用系统动画API,减轻CPU负担。 -
避免主线程阻塞:将耗时操作(计算、IO)放入Web Worker(前端)或Worker线程(鸿蒙)。
-
图片优化:使用尺寸适当的图片,避免内存浪费和GPU过载6。
-
列表性能:复用组件、实现懒加载、预加载合理数量的项(
cachedCount
)。 -
状态管理:鸿蒙中使用
@ObjectLink
替代@Prop
以减少深拷贝;实现局部刷新,控制UI更新范围
6 鸿蒙中Frame具体实操
开发应用或元服务过程中,如果发现有表单滑动不顺畅、页面交互延迟、动效不流畅等卡顿现象时,可以使用DevEco Profiler提供的Frame场景分析能力,录制卡顿过程中的关键数据进行分析,从而识别出导致卡顿丢帧的原因。此外,Frame任务窗口还集成了Time、CPU、Network场景分析任务的功能,方便开发者在分析丢帧数据时同步对比同一时段的其他资源占用情况。
说明
- 在任务分析窗口,可以通过“Ctrl+鼠标滚轮”缩放时间轴,通过“Shift+鼠标滚轮”左右移动时间轴。或使用快捷键W/S放大或缩小时间轴,使用A键/D键可以左右移动时间轴。
- 将鼠标悬停在泳道任意位置,可以通过M键添加单点时间标签。
- 鼠标框选要关注的时间段,可以通过“Shift+M”添加时间段时间标签。
- 在任务分析窗口,可以通过“Ctrl+, ”向前选中单点时间标签,通过“Ctrl+. ”向后选中单点时间标签。
- 在任务分析窗口,可以通过“Ctrl+[ ”向前选中时间段时间标签,通过“Ctrl+]”向后选中时间段时间标签。
- Frame分析支持离线符号解析能力,请参见离线符号解析。
- Frame分析支持能耗分析,请参见能耗分析。
查看GPU使用情况
- 创建Frame分析任务并录制相关数据,操作方法可参考性能问题定位:深度录制,或在会话区选择Open File,导入历史数据。
- “Frame”泳道显示当前设备的GPU的使用率,将其展开,子泳道显示Render Service侧帧数据和App侧帧数据。
说明
- 一帧的绘制,一般需要由App侧提交渲染到Render Service侧,然后Render Service侧再提交给硬件进行合成渲染,因此App侧的帧和Render Service侧的帧存在关联的情况。并且可能多个APP侧的帧/同一APP侧的多个帧提交到同一个Render Service侧帧上,出现帧之间的一对多的关联情况。
- 一帧绘制的期望耗时,与fps的大小有关,一般情况下fps为60,对应的Vsync周期为16.6ms,即App侧/Render Service侧的帧耗时,一般需要在16.6ms以内。App侧帧/Render Service侧帧判断卡顿的标准为帧的实际结束时间晚于帧的期望结束时间。
- 在“RS Frame”和“App Frame”标签的泳道中,正常完成渲染的帧显示为绿色,出现卡顿的帧显示为红色。
- 除“RS Frame”和“App Frame”泳道外的“ArkTS Callstack”、“Callstack”、“CPU Core”等泳道信息,请参考基础耗时分析:Time分析、CPU活动分析:CPU分析。
查看指定时间段内所有进程的Frame数据统计信息
- 在时间轴上拖拽鼠标选定要查看的时间段。
- 框选Frame主泳道。
窗口下方的“Statistics”区域中会以进程维度对选定时间段内的Frame信息进行统计,包括卡顿率、卡顿次数、最大连续卡顿次数、最大卡顿耗时、平均卡顿耗时以及平均正常耗时等。
- 点击“Statistics”列表中任一进程的跳转按钮,在“Frame List”区域将展现该进程对应的Frame列表。体现各帧的起始时间、总耗时、GPU耗时以及卡顿丢帧类型。
- 单击“Frame List”列表中任意一帧,右侧的“More”区域会中显示该帧更多关键信息。在获取该帧的预期起始时间、预期持续时间之外,您可以单击
跳转至关联的切片。
查看指定Frame页面布局信息
从DevEco Studio 5.1.0 Release版本开始支持查看最新录制的Session中的指定Frame页面布局信息。
暂不支持在Wearable设备上查看指定Frame页面布局信息。
- 单击RS Frame泳道或APP Frame泳道中任意一帧,”Details”区域中会展示该帧具体信息。单击Download Layout或Open Layout按钮,下载或打开页面布局信息。点击Open Layout按钮,将在ArkUI Inspector中直接打开相应arkli文件。点击Download Layout将arkli文件下载保存到指定目录,之后可手动导入ArkUI Inspector查看页面布局信息。
说明
单击“Download Layout”或 “Open Layout”前,需应用进程置于前台,才能正确回放全量渲染数据,获取arkli文件,arkli文件用于ArkUI Inspector展示页面布局信息。
- 在ArkUI Inspector中可查看组件树和组件属性信息,当前支持BackgroundFilter、nodeGroup、nodeGroupReuseCache等组件属性:
- BackgroundFilter:背景滤波器。
- nodeGroup:节点组类型,0表示非节点组节点,1表示被动画标记的节点组,2表示被UI标记的节点组,4表示被用户标记的节点组,8表示被前景滤波器标记的节点组。
- nodeGroupReuseCache: 0表示在生成缓存或无需缓存,1表示在重用缓存。
查看指定时间段内指定进程的Frame数据统计信息
- 在时间轴上拖拽鼠标选定要查看的时间段。
- 选择要观察的子泳道(例如带“RS Frame”标签的泳道)。
窗口下方的“Details”区域中会显示选定时间段内的RS帧统计信息列表,体现各帧的起始时间、总耗时、GPU耗时以及卡顿丢帧类型。
- 单击列表中任意一帧,右侧的“More”区域会中显示该帧更多关键信息。在获取该帧的预期起始时间、预期持续时间之外,您可以单击
跳转至关联的切片。
查看指定Frame信息
在子泳道(例如带“APP Frame”标签的泳道)中选中要查看的Frame,该泳道上方是耗时最长的非UI函数,下方是UI主线程泳道。
窗口下方的“Frame”区域中会显示选定帧的关键信息,如VSync编号、开始时间、App应用侧持续时间、App应用侧业务逻辑耗时、Render Service侧持续时间、GPU持续时间、总持续时间、卡顿丢帧类型以及可能出现卡顿的原因等。”Non UI”区域中会显示非UI耗时最大的函数,如开始时间、结束时间、持续时间,函数名等。
说明
- 在选定观察对象后,DevEco Profiler会自动关联与其相关的切片,用箭头连接。
- 如果该帧是由于超出期望结束时间引起的,则显示两条线,对应期望开始时间(Expected Start)和期望结束时间(Expected End),用于关联分析同一时刻Trace或者函数采样信息。
- 将鼠标悬浮在任意帧上,会冒泡显示该帧的Jank信息。
- 卡顿丢帧类型(Jank Type):No Jank(不卡顿)、AppDeadlineMissed(App侧的卡顿)、RenderDeadlineMissed(Render Service侧的卡顿)。
查看屏幕帧率动态变化场景下丢帧和卡顿信息
Frame泳道下新增Lost Frames和Hitch Time两类子泳道,用于识别和优化卡顿和丢帧现象。
- Hitch Time:展示当前时间段内卡顿时长。计算方式为渲染前后两帧的间隔减去单帧耗时,若计算结果大于单帧耗时*70%,则视为出现卡顿现象。
- Lost Frames:展示当前时间段内丢帧数。Lost Frames计算出的结果,六舍七入统计取整。
- 创建Frame模板并录制会话,如存在卡顿和丢帧现象,会在Lost Frames和Hitch Time泳道对应时间显示矩形图。
- 鼠标点选某一时间点,提示信息会显示该点所属时间段内的丢帧数以及卡顿时间。
支持动效场景调优
开发者在开发应用时,会使用到动效,动效的卡顿影响到用户的使用体验。DevEco Profiler提供动效场景的调优,能帮助开发者优化动效场景。
鼠标放置在某个动效上,显示该动效的详细信息,包括响应时延、动效持续时间、完成时延、期望帧率、FPS。
说明
- 响应时延:<=85ms 绿色,85ms~150ms 浅绿色,150ms ~250ms 浅红色,>250ms深红色。
- 期望帧率:当前系统运行满帧帧率,如60HZ、90HZ、120HZ。智能刷新率模式下,不展示期望帧率。
- 动效持续时间:根据帧率展示颜色,FPS大于达标帧率即为为绿色,小于则为深红色。智能刷新率模式下,帧率可变,颜色为灰色。达标帧率与期望帧率的大小有关,一般情况下期望帧率为60HZ,则达标帧率= 60HZ * 91.7%。
- 完成时延:响应时延和动效持续时间只要有一个为深红色,完成时延为深红色。
- Launch模板中Frame泳道点击detail区启动动效详情信息,more区域展示动效帧Animation Data List信息。
查看组件动画信息
从DevEco Studio 6.0.0.828版本开始,Frame泳道下新增Component Animation子泳道,用于从组件的角度展示应用中包含的各种动画类型,包括属性动画 (animation)、显式动画 (animateTo)、关键帧动画 (keyframeAnimateTo)以及页面间转场 (pageTransition)。
在Details页签中,可以查看每个动画的详细信息,包括起止时间、帧率、动画曲线类型以及影响的组件属性等。
单击列表中任意一动画,右侧的“More”区域会中显示该动画所影响的组件属性的具体变化过程。
查看组件帧率信息
Frame泳道下新增两类子泳道,分别为Display Vsync与DisplaySync_cb(tid),用于对可变帧率的检测调优。
- Display Vsync:该泳道显示对应时间段的屏幕刷新率,支持对框选的时间段内的vsync进行分布统计。区分”<=30HZ”、”30~60HZ”、”60~90HZ”、”>90HZ”。统计值包括框选时间段内各区间的分布比率、最小/最大/平均时长以及平均HZ。如果某场景满足了帧率改变的要求,当底层系统根据机制进行变帧,相应的情况会展现在对应的泳道,帮助开发者了解vsync的变化情况是否符合预期。该泳道仅支持在配备硬件屏幕的设备上进行数据采集。
- DisplaySync_cb(tid):该泳道显示对应组件的帧率,如DisplaySync、XComponent两类接口组件动画对应的帧率。调测时,不同场景下由于帧率可变,系统实际表现是否符合预期,需要有实际的检测手段。尤其是由于DisplaySync的渲染均在UI主线程执行,当存在多个需要渲染的组件需要同时执行时,只能在UI主线程排队,此时任何一个组件的延迟都会对其他组件的渲染产生影响,导致UI卡顿。
如下图所示,vsync2和vsync4中,vsync周期内的组件由于渲染耗时长,导致以下两个vsync周期挤掉下一个vsync周期的渲染时间,导致掉帧的情况产生。
- 选择Display Vsync泳道,在时间轴上拖拽鼠标选定要查看的时间段。
- 详情区显示当前时间段的屏幕刷新率,当前帧最大持续时间、最小持续时间、平均持续时间以及该时间段内平均帧数。
- 点选Display Vsync泳道,可以查看当前帧的耗时和帧率。
- 框选DisplaySync_cb泳道,可以查看应用侧对应组件的帧率,渲染时间等信息。
- 同时如果组件有可能的掉帧情况,DisplaySync_cb泳道显示对应的掉帧情况并标红展示。
查看帧率统计信息
Frame泳道中的App Frame泳道和RS Frame泳道在框选时新增fps标记。RS泳道新增过滤按钮,用于过滤ArkWeb数据。
- 展开Frame泳道,框选一段数据。
- 泳道出现fps标记,展示当前框选范围内的帧率统计信息。
- 打开Only ArkWeb data开关,筛选过滤出包含ArkWeb帧的数据。
Anomaly泳道:查看解码过度耗时和超过阈值的序列化、反序列化操作
如果工程中存在图片资源,并感知到解码绘制/渲染过程存在卡顿,可以通过Anomaly泳道查看主线程解码过程中是否存在解码过度耗时告警,并确认发生告警的时段。
如果应用中使用了worker, Taskpool工作线程等场景,通常会触发跨线程对象传递,并触发序列化和反序列化的操作。对于耗时超过阈值的序列化、反序列化操作,Anomaly也会给出对应的耗时告警,并给出发送这个操作的开始时间和耗时时间。
- 在时间轴上拖拽鼠标选定出现告警的时间段。当耗时超过VSync周期的50%时,将在Anomaly泳道中出现红色告警,提示“Image decoding has exceeded 50% of the VSync time“。
- 详情区给出录制时段内解码过度耗时的统计情况,包括类型,图片名,计数,总耗时,最小耗时、平均耗时、最大耗时,耗时标准差、 图源尺寸大小,目标尺寸大小等。
- 对于耗时超过阈值的序列化、反序列化操作,Anomaly也会给出对应的耗时告警。其中可以通过泳道启动配置按钮配置检测阈值,默认配置阈值为8ms。
- 详情区给出录制时段内序列化、反序列化耗时情况统计信息,包括类型、计数、总耗时、最小耗时、平均耗时、最大耗时、耗时标准差等。
说明
已上架应用市场的应用不支持录制Anomaly泳道。
User Events泳道:查看用户事件耗时
开发者在卡顿丢帧场景可通过User Event用户事件,查看用户事件开始时间、应用开始处理时间以及应用处理耗时等情况。
- 选择User Event泳道,在时间轴上拖拽鼠标选定要查看的时间段。
- 详情区列表给出录制时间段内用户事件详情,包括用户事件ID、事件开始时间Input Time、应用开始处理时间Processing Start、应用处理耗时Duration和事件类型User Event Type。
- 点选User Event泳道中的条块,Slice详情区展示该事件的详情信息。
7总结
Frame分析是保障应用流畅性的关键手段。前端开发依靠浏览器强大的DevTools,而鸿蒙开发则借助DevEco Studio内置的Frame Profiler等深度集成的工具。虽然工具和平台不同,但优化的核心思想是相通的:识别卡顿帧、定位瓶颈、针对性优化
华为开发者学堂