冷启动两秒半,首帧还没出来数据库先读了两百条
“启动慢”拆开看,大头在首帧之前
截图整理工具加了一个首页,展示最近导入的截图和分类概览。冷启动计时从点击图标到首页可交互,实测 2.8 秒。第一反应是系统慢,用 SmartPerf 拆开时间线,进程创建和系统初始化只占了 0.4 秒,剩下 2.4 秒里,首帧渲染和数据加载占了大头。
首页的 aboutToAppear 里做了三件事:同步读了两百条截图摘要、初始化了 OCR 和超分引擎、预加载了第一批缩略图。这三件事全部排在 build() 之前,首帧被硬生生推迟了。

首帧只画“壳”,数据异步补齐
aboutToAppear 的执行时机在 build() 之前。任何放在这里的同步 IO、大循环、第三方 SDK 首次初始化,都会原封不动加到首帧时间上。首帧只需要“能画出第一屏”的最小数据,其余全部后移。
typescript
@Entry
@ComponentV2
struct HomePage {
@Local skeletonVisible: boolean = true;
@Local recentShots: ScreenshotItem[] = [];
aboutToAppear(): void {
// 首帧只需要骨架屏,不读任何重数据
setTimeout(async () => {
this.recentShots = await loadRecentShots(20);
this.skeletonVisible = false;
}, 0);
}
build() {
if (this.skeletonVisible) {
HomeSkeleton()
} else {
RecentList({ data: this.recentShots })
}
}
}
setTimeout(..., 0) 在这里不是“延时”,是把任务从首帧关键路径上摘出去。骨架屏让用户感知到“正在出来”,而不是对着一片白猜应用是不是卡死了。首页两百条数据 ForEach 全量渲染的案例,只加载首屏十条、其余延迟拉取之后,首帧从 1.1 秒降到三百毫秒级别。
Application 初始化只保留首屏必需的
EntryAbility.onCreate 里串行初始化六个模块的写法,在开发阶段看不出来,真机冷启动直接多出半秒。网络请求首屏必需,留下;存储、埋点、推送、IM、地图按需延迟,用 setTimeout 或 TaskPool 分批启动。
typescript
async onCreate(want: Want): Promise<void> {
await initNetwork(); // 首屏请求必需,保留
setTimeout(async () => {
await Promise.all([
initStorage(),
initAnalytics(),
initPush()
]);
}, 0);
// OCR / 超分引擎按需,在用户首次使用对应功能时再初始化
}
不要把所有初始化都延迟——真正首帧需要的依赖必须同步完成,否则会出现白屏或跳变。延迟初始化也要做回归,避免引入“首次点击时才加载”的次生卡顿。
数据预加载看命中率,低于三成下沉为用时加载
首页的“最近截图”可以读缓存,这个读取是首帧必需的。但“全部分类统计”和“特征向量预热”这类数据,如果用户十次启动只有两次会点到,就不值得在冷启动时同步加载。判断标准是预加载命中率:如果某个预加载任务命中率低于 30%,或者让冷启动 P95 明显变差,就应该下沉为用时加载。
冷启动的优化不是把代码改乱,是把“首帧不需要的东西”和“首帧需要的东西”分开。Application 初始化拆成必需和延迟两批,首屏数据只取最小集,其余交给骨架屏之后异步补齐,再配合包体积和增量编译的构建侧优化,两秒半的启动压到一秒以内是工程上可达的目标。验收时看两个数字:首帧时间有没有降,延迟初始化有没有引入新的首次点击卡顿。两个都稳,优化才算成立。
更多推荐
所有评论(0)