简介
在应用开发中,有的页面需要加载大量的数据,就会导致组件数量较多或者嵌套层级较深,从而引起组件负载加重,绘制耗时增长,如果不进行合理的处理,可能引起卡顿掉帧等性能问题。
问题场景
在日历应用的开发中,全年的日期页面需要加载一年中的所有日期,这样就最少需要365个Text组件用于显示日期。一次性绘制这么多的组件,时间会比较久,而且会耗费大量的资源,如果手机配置较差,可能会引起明显的卡顿或者转场动画的掉帧现象。
解决思路
由于一次性加载大量数据、绘制大量组件会导致卡顿,那么减少加载的数据量就是一种解决方法。但是由于业务需求,需要加载的数据总量和绘制的组件数量是不能减少的,那么只能想办法将数据进行拆分,将和数据相关的组件分成多次进行绘制。ArkTS中提供了DisplaySync(可变帧率),支持开发者设置回调监听,可以在回调里做一些数据的处理,在每一帧中绘制少量的数据,减少卡顿或者转场动画的掉帧现象。
优化示例
常规代码
通常情况下,会在进入页面后开始加载数据,即在aboutToAppear()中加载所有数据,并通过LazyForEach绘制所有的组件。
@Entry
@Component
struct Direct {
...
// 初始化日历中一年的数据
initCalenderData() {
// 添加自定义trace标签,用于在trace抓取结果中查看相关运行时间信息
hiTraceMeter.startTrace('push_data_direct', 1);
for (let i = 1; i <= 12; i++) {
// 获取每个月的日数据
const monthDay: number[] = getMonthDate(i, this.currentYear);
const month: Month1 = {
month: i + '月',
num: i,
days: monthDay
}
this.contentData.pushData(month);
}
hiTraceMeter.finishTrace('push_data_direct', 1);
}
aboutToAppear() {
...
this.initCalenderData();
}
build() {
Column({
space: 12 }) {
...
Grid() {
LazyForEach(this.contentData, (monthItem: Month1) => {
// 每个月的日期
GridItem() {
Flex({
wrap: FlexWrap.Wrap }) {
...
// 日期信息
ForEach(monthItem.days, (day: number) => {
Text(day.toString())
...
})
}
...
}
}
...
}
}
在上面的代码中,要在页面上显示一年中的所有日期,在aboutToAppear()方法中,将每个月的信息放入到一个数组里面,并通过LazyForEach通知Grid进行绘制。编译运行后,通过SmartPerfHost工具,抓取Trace,并查看耗时和掉帧率,如图1所示。其中push_data_direct是自定义添加的Trace标签,可以看到加载数据的开始时间和耗时,Expected Timeline是期望绘制一帧的时间,Actual Timeline是实际绘制一帧的时间。
图1 直接加载所有数据Trace图
通过图中信息可以看到,在aboutToAppear()中直接加载全部数据时,实际上就是在一帧中绘制