滑动页面占位符加载完成时延问题分析思路&案例
1. 场景导入
滑动页面占位符加载完成时延:可滚动页面中,滚动停止开始算起,到屏幕内占位符(一般为图片)加载完成。
2. 性能指标
2.1 性能指标介绍
滑动页面占位符加载完成时延的S标为40ms。
2.2 性能衡量起始点介绍
数帧工具:Avidemux 2.6 - 32 bits (32-bit)
通过视频抓取滑动停止为起始点:
通过视频抓取占位符加载完成为终止点:
根据终止点事件减去起始点事件计算完成时延:5161-4671 = 490ms
3. 问题定位流程
3.1 常规定位前置流程
处理三方应用问题前首先需要先和三方应用及测试确认当前问题场景的静态KPI标准(S标):
- 三方应用:和三方应用确认问题场景是否认可该标准,如不认可,相关问题需评审关闭。
- 测试:和测试确认是否按照静态KPI标准执行的测试,测试步骤和性能衡量是否准确。
处理三方应用问题时,可以优先查看操作录屏,查看操作场景,看能否发现一些有助于定位的信息,比如滑动过程中是否存在图片加载动画,是否包含网络请求等等。
冷启动Trace抓取请参考"附录1: Trace抓取方式"。
3.2 问题定位思路
滑动操作占位符完成时延类问题的通用定位思路为先确认时延起止点,然后看起止点时延是否超40ms,没有超过就是达标,如果超过40ms,就需要进一步分析Trace看看耗时主要发生在什么地方,主要分析网络耗时和帧渲染耗时,最后确定是系统问题还是三方问题。处理流程如下图:
加载完成时延起始点:APP_LIST_FLING终点视为滑动停止,则是加载完成时延起始点。
滑动页面占位符加载完成,是以滑动停止为起始点,在Trace中APP_LIST_FLING泳道可以体现滚动视图的FLING惯性滚动状态的起止,惯性滚动停止则滚动停止,此时开始计算占位符加载时延。
查找步骤:
- 搜索APP_LIST_FLING,
- 找到APP_LIST_FLING泳道,星标后即可置顶查看。
- Trace标记了惯性滚动区间。
- APP_LIST_FLING结束点=加载完成完成时延起始点。
加载完成时延终止点:APP_LIST_FLING终点视为滑动停止后,图片加载完成即页面不再发生变化(应用侧不提交Vsync信号到RenderService),则是加载完成时延终止点。
滑动页面滚动停止后,会出现两种情形。
- 未触发上拉加载,滚动停止后的第一帧,分析异常帧。
- 触发上拉加载,分析网络请求,分析异常帧。
精确定位终止点流程。
应用主线程泳道:H:ReceiveVsync —> H:SendCommands
Render Service泳道: H:RSMainThread::DoComposition
RSHardwareThread泳道:H:Commit
加载完成时延:起始点与终止点时间间隔。
3.2.1 找问题点
如果从应用UI上发现有网络加载的动作,则可以在ArkTS CallStack泳道查找是否发送网络请求,关键Trace点createHttp,继续查找请求响应点off(request),parse数据解析,OnDataReload(LazyForEach刷新数据)来判断请求结束数据刷新时间点。因为在长列表应用中,一般使用分页加载功能实现更多数据,在滚动停止或者将要停止时触发加载更多功能,发送网络请求,收到响应数据后解析并刷新数据源,驱动页面刷新。
在FLING结束点,往后查看ArkTS CallStack调用栈,查看耗时任务,如发现耗时任务,则继续查看耗时原因,一般结合应用进程UI主线程查看;如未发现耗时任务(比如idle状态),则查看此时Frame应用侧是否有渲染任务和对应的Component组件情况,一般idle状态应用送帧情况为动画。
在FLING结束点,往后查看Frame应用侧帧渲染情况,是否出现异常帧、超长帧。此时一般结合应用进程UI主线程查看具体的组件渲染详情。
泳道 | 问题1 | 问题2 | 问题3 |
Frame/应用线程 | 送帧平滑 | 超长帧 | 异常帧 |
ArkUI Component | 组件极少 | System和Custom组件极多 | System组件居多 |
ArkTS CallStack | 空闲+request | 任务 | 任务 |
异常追踪 | 单一组件动画,后台任务网络请求 | 大量组件创建或刷新渲染 | 系统组件创建或刷新渲染 |
3.2.2 根因分析方法
- 滑动停止有网络请求,则考虑网络时延。
- 滑动停止有出现超长帧、异常帧耗时,考虑复用机制失效或者冗余嵌套渲染时延。
- 列表中Item占位符图片占位符常常加载的是网络图片,则考虑网络时延。
- 占位符图片在加载过程中使用动画,会导致渲染完成时延,比如透明度0到1,缩放比例0到1,则考虑动画时延。
4. 常见根因归档
4.1 因网络加载导致占位符加载完成时延不满足S标
4.1.1 问题场景分析
滑动页面触发上拉加载,在loading动画期间等待数据请求,数据请求完成后刷新列表,占位符加载完成时延不满足S标。
4.1.2 问题根因分析
根据起始点确定问题Trace起始点和终止点,如下图加载完成时延总共4s。
网络请求数据耗时根据场景上拉加载更多,数据通过网络请求后刷新,放大Trace找到APP_LIST_FLING尾部,末尾触发request请求数据,即滚动到尾部将要停止时会触发上拉加载,发送请求获取网络接口数据。
发送请求request:
发送网络数据请求后,会有Response体现在应用中则是解析后刷新数据,LazyForEach绑定的IDataSource会触发刷新监听,通过OnDataReloaded找出刷新数据Trace点,可得到网络请求耗时495ms。
开始刷新数据OnDataReloaded:
网络图片加载耗时本案例中列表中主要占位符为Image组件,加载是通过ImageSource解码生成PixelMap。加载网络图片时,发送图片地址网络请求,接着将返回的数据解码为Image组件中的PixelMap。通过搜索CreateImagePixelMap搜索创建图像像素图,耗时26ms。
加载网络图片资源 CreateImagePixelMap:
4.1.3 优化方案
- 由于网络时延受多方面因素影响,可尝试优化网络请求和网图加载。
- 采用预请求方式加载更多数据,在快滑动底部某个位置时触发请求,用户无感加载更多,缩减网络请求等待。
- 列表图可适当采用小图模式加载,加速网图资源下载速度。
- Image组件alt属性加载本地占位图,避免占位符在等待网图加载期间空白。
- 图片缓存机制,再次加载网图可从缓存中取图。
4.1.4 问题总结
占位图加载完成时延,一般受首次网络请求时延影响,如果二次加载图片完成实验<=40ms,也算达到S标。
4.2 因组件渲染导致占位符加载完成时延不满足S标
4.2.1问题场景分析
在滚动到底部时,上拉加载更多的网络请求,等待网络请求数据完成后驱动UI刷新。实际测试中发现,上拉加载次数越多,占位图加载完成耗时就越久,可以推断出在加载更多数据后的渲染有异常。
4.2.2 问题Trace特点
分析Trace发现列表每次滚动停止触发上拉加载后,会有一个超长帧。
分析Trace中应用主线程泳道超长帧,发现有大量组件创建和布局测算。
- 在应用主线程泳道超长帧前,有关键刷新Trace点:OnDataReloaded(LazyForEach通知控制器数据重新加载)。
- 在ArkTS Callstack中查看调用栈发现,js调用notifyDataReload,说明应用侧此时触发了页面刷新。
- 在应用主线程泳道两次超长帧加载LazyItem创建索引可以发现,0-13到0-33,单帧绘制的item越来越多。
总结如上3点,结合实际场景上拉加载次数越多,时延越久,说明应用侧使用了全量数据刷新。
详细Trace分析如下:
OnDataReloaded开始触发UI刷新。
查看超长帧,第一次上拉加载更多。
查看超长帧,第二次上拉加载更多。
继续分析超长帧,通过应用主线程泳道发现单帧发现有大量BuildItem构建GridItem,而且在懒加载LazyForEach predict中大量aboutToBeDeleted发现析构处理,说明GridItem在滑动过程中被释放。从而分析出列表中子组件未做复用影响性能。
BuildItem构建GridItem:
LazyForEach predict中aboutToBeDeleted析构GridItem:
4.2.3 优化方案
- 可以采用组件复用机制@Reusable优化性能。
- 优化LazyForEach的键值刷新规则,采用onDataAdd局部更新。onDataReloaded会通知组件重新加载所有数据,键值没有变化的数据项会使用原先的子组件,键值发生变化的会重建子组件。
4.2.4 问题总结
占位符Image组件加载完成需要通过UI渲染,优化滑动过程中UI组件渲染效率可提高占位符加载完成效率。
4.3 因组件动画导致占位符加载完成时延不满足S标
4.3.1 问题场景分析
在滑动列表过程中,占位符图片加载明显看出从无到有的渐变动画。
4.3.2 问题Trace特点
分析Trace滑动过程中的每一帧,发现在GridItem加载过程中使用了自定义动画,查看JSAnimation动画参数duration为150ms,说明此动画完成时间为150ms,影响图片的加载效果。
动画JSAnimation:
4.3.3 优化方案
评估动画是否合理或者优化参数。
4.3.4 问题总结
在UI显示阶段,动画是影响响应时延类的重要因素。