【鸿蒙心迹】冷启动优化实战:两秒的空白去了哪,又怎么压到一秒

前言

深色模式收尾的时候说过,性能这条线还剩最后一个大件:冷启动。动机很直接——内测群有用户留言"点开图标要等两秒多,感觉像死机"。两秒多,这个数字在开发机上完全感受不到,但它是三位真机用户的共同体感。这篇记录把冷启动从 2.3 秒压到 1.1 秒的全过程,方法朴素到有点扫兴:先拆账,再动刀,全程没有用到任何高级技巧。

环境照旧:DevEco Studio 5.x、HarmonyOS NEXT、API 14+。启动框架与打点 API 以官方《应用启动框架》文档为准,数字为真机实测口径。

时间账:那段空白去了哪

优化之前先回答一个问题:点击图标到首帧可交互,这两秒多花在哪了?三行打点就够——AbilityStage 的 onCreate 打一个时间戳,首页 aboutToAppear 打一个,首帧回调打一个,三段账目一目了然:

拆出来的账很难堪:进程创建和系统渲染这两段动不了也无需动,肥肉全在自己写的代码里——onCreate 里 580 毫秒,首屏构建加首次查询 700 毫秒。也就是说,用户等待的绝大部分时间,是在等我自己的初始化代码跑完。这个发现让人尴尬,也让人踏实:自己的代码,自己就能治。

顺便定下验证口径,这比优化本身更重要:真机、冷启动(杀进程重开)、十次取中位数。开发机上点图标的热启动不算数——它测的是缓存,不是冷启动;模拟器的数字更不算数,并发篇说过,它是另一个世界。之后每一刀改完,都回到这个口径重新量十次。

第一刀:onCreate 减负

拿着打点去看 onCreate,四行 init 语句:数据库初始化(含建表和迁移校验)、推送 SDK、统计 SDK、全量待办预加载。每一行单看都"应该"在启动时干——干了一年的代码没人怀疑过它。

用一把刀重新审它们:首帧画出来之前,用户看得到它吗?

看得到才留下。首屏确实需要数据——但只要今日待办的一条,不是全量预加载;确实需要库就绪——但建表迁移可以等首帧之后、首次真正查询之前再补。推送、统计、升级检查,首帧上没有任何一像素依赖它们,全挪。注意判断句式是"看得到",不是"马上要用"——"马上要用"这个说法弹性太大,人人都能给自己找到理由,"看得到"没有弹性。

挪完之后 onCreate 从 580ms 到 90ms。四行代码,三行本来就不急。启动优化的第一步往往就是这样,没有技巧,全是纪律:启动链路的每一行都是全体用户付费——哪怕百分之九十的用户永远不碰推送。

顺带做了个考古,值得写下来:全量查询是怎么混进 onCreate 的?git blame 显示它比推送 SDK 还早,最初首页确实需要"全部待办"来画统计饼图;后来首页改版成今日视图,查询顺手留在原地没人动。启动链路是考古地层——每一层 init 都对应某个历史版本的合理需求,需求走了,代码留下来。所以"减负"不能凭记忆,要逐行问"这行现在服务谁",答不上来的先挪出去再说。

连带审判:aboutToAppear 也在启动链路上

onCreate 瘦身之后,账目立刻指向下一段:首屏构建 700 毫秒里,页面本身只占两成,剩下五成多在 aboutToAppear——我曾在那里塞了统计计算、主题色预计算、还有一次"顺手"的全量查询副本。同一个审判标准直接平移过去:aboutToAppear 是启动链路的下半场,审判标准一模一样。首帧看得到才留,看不到挪走。

清完这两处,700 毫秒降到 240。整个过程没有一个"优化技巧",全是把放错位置的计算搬回它该在的时间段。这也解释了为什么冷启动问题几乎每个项目都有:不是开发者不会优化,是"启动时顺便干点活"的诱惑太大,而账单要很久之后才来。

第二刀:首帧之后的空闲时段

挪出去的任务不能丢,它们得在别处上车。车就是启动框架的空闲调度:首帧一过,系统进入空闲时段,按优先级把延迟任务逐个放行,用户一有交互就让路:

排队的顺序按用户真实使用顺序排:统计推送这类纯后台的最先(它们不抢用户能感知的资源),全量预加载第二,卡片快照预热和字体插画预取殿后。上车之后用户毫无感知——首帧已经上屏,晚一点没人知道。

两条纪律必须立住。其一,延迟不是丢弃:每个延迟任务都要有最终执行的保证,注册了就要跑到,否则某天用户直接进二级页面,发现依赖的全量数据从没加载过——这种 bug 报上来,排查路径极长。其二,依赖关系画清楚:全量预加载必须在"库就绪"之后,建表迁移必须比首次真实查询早——把谁等谁写进任务注册的顺序里,别靠"反正它快"的侥幸。

第三刀:首屏轻量占位

第三刀最舒服:首屏构建时,列表区先渲染骨架占位,真实数据由空闲时段的预加载完成后补上。这一手在卡片篇用过——onAddForm 先给"正在加载"的占位快照,真实数据随后补推。先给一张能看的,再给一张对的,用户全程无感,心理等待时间归零。

心理等待和物理等待是两笔账,这笔占位改的是前者。1.1 秒里用户在 0.6 秒左右就看到了完整的骨架和标题,"应用打开了吗"的疑问在那一瞬间就消散了。剩下的半秒,用户正在读今日第一条待办——等待被藏进了阅读里。

数据说谎的一次:0.9 秒的假胜利

第一刀改完测出来 0.9 秒,比预期还好,我差点直接写进周报。第二天复测变成 1.2 秒——同一台手机,同一份代码。回看记录发现第一天的十次里有六次特别快:那台手机前一天连着 USB 调试,开发者选项里的"充电时保持唤醒 + 加强性能档"还开着。关掉复测,数字立刻老实了。

从那以后验证清单多了三项:拔掉 USB、关掉性能档、杀干净进程。性能测量最大的敌人不是波动,是"系统悄悄帮你"——性能档、预热缓存、甚至电量高低都在改数字。测出来的好成绩如果解释不了为什么好,就当它没发生,这条和"体感变快必须回到中位数"是一体两面:解释不了的改善和解释不了的恶化,同样可疑。

诊断的固定套路

把三次优化的排查路径沉淀成一张流程图,下次"感觉慢"直接照着走:

核心思想一句话:诊断就是把一个模糊的"慢"翻译成一个明确的"块"。用户说"有时候快有时候慢",拆完账都是同一句话——"某一块占了多少毫秒"。只打最大的一块,改完再量;一次只动一处,数字动了才算数,"体感变快"必须能回到中位数上,否则等于没改。

踩坑三条

  1. 热启动冒充冷启动:验收时开发同学从最近任务里点开,数字漂亮得可疑。热启动有进程和缓存兜底,测的完全是另一回事——验收口径里"杀进程"三个字要写死;
  2. 延迟任务里的隐式依赖:预加载任务注册得早,跑的时候库还没就绪,偶发查询失败。解法是把依赖写成显式顺序(建表完成的回调里再注册预加载),别赌时序;
  3. 占位动画本身拖慢首帧:骨架屏用了逐帧动画,首帧反而比之前慢了 60ms——给"让首屏更轻"的东西自己变重了。骨架保持静态,动画留给内容出现之后。

收个尾

2.3 秒到 1.1 秒,三刀:onCreate 减负、空闲调度上车、首屏占位。没有任何一步用到"高级优化",全是拆账、二分、排队——启动优化的本质不是做得快,是只做必须的,剩下的活儿换一个用户看不见的时间上车。

照例自查清单,性能篇到此收束:

  • onCreate 里每一行,过得了"首帧看得到吗"的审判吗?
  • 延迟任务有最终执行的保证吗?依赖顺序是显式的吗?
  • 验收口径是真机、冷启动、十次中位数吗?改完回到数字上了吗?
  • 首屏有占位吗?占位本身比真内容还重吗?

至此,从踩坑复盘、状态管理、调试工具,到生命周期、卡片、包管理、一多、持久化、长列表、权限、网络、并发、深色、冷启动——码上清单的基础设施阶段全部收官。下一篇开新章:通知与后台任务,让应用"不在场"时也能把重要的事送到。下篇见。

*本文环境:DevEco Studio 5.x / HarmonyOS NEXT(API 14+)。启动框架、打点与调度 API 以官方文档为准,文中数字为真机冷启动十次中位数。系列上一篇:《【鸿蒙心迹】深色模式适配实战》,下一篇:通知与后台任务。*

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐