HarmonyOS 鸿蒙Next中APP冷启动优化探索
HarmonyOS 鸿蒙Next中APP冷启动优化探索
分析鸿蒙应用冷启动耗时
应用冷启动过程大致可分成以下五个阶段:
- 应用进程创建&初始化
- Application&Ability初始化
- Ability/AbilityStage生命周期
- 加载绘制首页
- 网络数据二次刷新
应用进程创建&初始化阶段
该阶段主要是系统完成应用进程的创建以及初始化的过程,包含了启动页图标(startWindowIcon)的解码。
Application&Ability初始化
该阶段主要是资源加载、虚拟机创建、Application&Ability相关对象的创建与初始化、依赖模块的加载等。
Ability/AbilityStage生命周期
该阶段主要是AbilityStage/Ability的启动生命周期,执行相应的生命周期回调。
加载绘制首页
该阶段主要是加载首页内容、测量布局、刷新组件并绘制。
网络数据二次刷新
该阶段主要是应用根据业务需要对网络数据进行请求、处理、二次刷新。
检测app的启动耗时
鸿蒙app的冷启动耗时有两种统计方法:
- 订阅启动耗时事件
- 自动化工具(TeslaLab)
订阅启动耗时事件
其中我们只要关注两个点即可:
- icon_input_time:点击桌面应用图标离手时间戳,单位为毫秒。
- extend_time:开发者定制的启动耗时,该时间为手指离开屏幕到开发者调用reportDrawnCompleted接口的时间。
我们可以根据业务需要在合适的时机调用reportDrawnCompleted接口,不调用或手指离开屏幕后5s未调用则该指标大小为0。reportDrawnCompleted接口放在首页渲染完成即可,不过如果我们已经监听了首页渲染回调的话也不需要再调用reportDrawnCompleted,自己记录时间戳计算即可。
自动化工具(TeslaLab)
TeslaLab自动化可以多跑几次分析启动数据,还可以一起跑下竞品的数据做对比
识别启动缓慢问题
分析启动过程的耗时瓶颈,优化冷启动速度时,可使用Profiler的Launch场景分析功能,录制启动过程中的关键数据,识别启动缓慢的原因。Profiler Launch可拆解应用冷启动过程,抓取各阶段的耗时数据,帮助快速分析冷启动过程的耗时瓶颈。 Launch的具体使用方法参见冷启动分析:Launch分析,录制一段Launch任务,具体操作步骤请参考性能问题定位:深度录制。
分析主线程的Trace数据
- 单击“Launch”泳道上的UI Ability OnForeground阶段,在下方“Details”面板中可查看所选阶段的耗时统计。
- 展开UI Ability OnForeground统计信息折叠表,可查看各函数的具体耗时信息。
- 根据Duration找到耗时最长的函数aboutToAppear。
- 单击图标按钮,可直接跳转至主线程的打点任务,查看相关Trace数据。
分析采样得到的函数热点
也可以分析采样得到的函数热点直观的显示应用冷启动过程中具体函数的耗时
- 单击“Launch”泳道的UI Ability OnForeground阶段。
- 选择“ArkTS Callstack”泳道,它会基于时间轴展示CPU使用率和状态变化,以及当前调用栈名称和类型。
- 在“Details”详情面板中,可以查看这段时间内的函数热点,以Top-Down形式的树状列表展示。computeTask函数在aboutToAppear函数中耗时最多,占整个阶段的96.7%。双击该函数可跳转到源码。
冷启动速度优化方案:
1、缩短应用进程创建&初始化阶段耗时
官方建议启动页图标分辨率不超过256像素*256像素
2、缩短Application&Ability初始化阶段耗时
该阶段主要耗时点在于资源加载阶段,分为主要的三个步骤:文件加载、依赖模块解析、文件执行。
减少import *的方式全量引用
包括import * 和 from '…/…'方式 可以减少同一个文件中所有export变量的初始化过程: 应用程序加载过程中,需要使用不同模块中的变量或函数,通常应用开发者会将相同类型的变量或函数放在同一个工具类文件当中,使用时通过import的方式引入对应的模块,当工具类中存在较多暴露函数或变量时,推荐直接import对应的变量,可以减少该阶段中.ets文件执行耗时。
减少使用嵌套export *的方式全量导出
由于依赖模块解析采用深度优先遍历的方式来遍历模块依赖关系图中每一个模块记录,会先从入口文件的第一个导入语句开始一层层往更深层查找,直到最后一个没有导入语句的模块为止,连接好这个模块的导出变量之后会回到上一级的模块继续这个步骤,因此多层export *的使用会导致依赖模块解析、文件执行阶段耗时增长。
合理拆分导出文件,减少冗余文件执行:
主要是将HAR包的导出文件Index.ets进行按需拆分
优点
使用此种方案优化后可以将冷启阶段(加载首页文件)与非冷启阶段(加载非首页文件)需要执行的.ets文件进行完全拆分,类比其他需优化的场景也可以使用本方案进行拆分。
缺点
需保证拆分后IndexAppStart.ets中的导出文件不存在对于IndexAppLazy.ets中的导出文件的引用。
或者将冷启动相关文件使用全路径展开
优点
不需要新增文件来汇总导出所有冷启阶段文件。
缺点
引用时需要对所有冷启阶段文件进行路径展开,增加开发和维护成本。
减少启动冗余import文件【重点优化项】
ArkTS冗余文件检测
运行结果会得到耗时文件列表 文件分为三部分:启动2s内加载的所有文件以及耗时 used file 和unused file部分,file列表是按照加载顺序排列的,清晰列出每个import的耗时以及引用链 为了直观的查看哪些import最耗时,可以将列表按照cost time从大到小进行排列,搞个排序脚本即可
优化方式
把冷启不需要的文件延迟加载
Lazy-Import与动态加载选择
动态import是一种模块加载机制,允许应用程序在运行时按需加载相关模块。当特定条件满足时(如用户交互或ABTest分支切换),再加载所需模块,可减少初始化加载时间和资源消耗,提高应用程序的内存性能和响应速度。 与静态import不同,动态import仅在需要时消耗资源。动态import在编译时不确定引入的模块,语法更灵活,支持代码和路由级别的粒度分割,优化懒加载性能。 在使用动态加载时,需要将静态加载的代码(同步导入)改写成动态加载语法(异步导入),修改量较大。 如果希望通过动态加载在冷启动阶段产生优化,需要明确感知被动态加载文件不会在冷启动时执行,否则会增大冷启动开销(放入异步队列等)。 相较于动态加载,使用Lazy-Import延迟加载,只需要在import语法中增加lazy关键字,使用更加方便。
Lazy-import问题
-
暂不支持延迟加载kit–系统库不支持lazy-import,但可通过间接方式实现,下面系统库优化会讲到
-
lazy import 嵌套、不同文件引用同一文件
-
在A.ets文件中中, import lazy { B } from “./B”; 在B.ets文件中import了 C.ets、D.ets、E.ets。。。CDE这些文件会自动被延迟加载,不需要再次添加lazy
-
在A.ets文件中, import lazy { B } from “./B”; 同时在C.ets文件中 import { B } from “./B”; 这时要看A文件和C文件谁先执行,如果C文件冷启动会执行,那么B文件还是会被加载,如果想在加载B的时候延迟加载CDE,就也要给他们添加,跟他的执行顺序有关系
-
lazyimport 后就变成同步加载了,需要慎用,使用不当会增加耗时
-
每次生成的数据都不一样,即使在代码中固定时机杀掉APP,生成了也是不一样的
系统库和三方库加载优化
系统库和三方库的优化是最为明显的,可以作为优化重点
系统库和三方库导入规范
系统库和三方库直接import会增加启动耗时,可根据是否是启动必须项判断导入方式,
- 非启动项选择lazy-import或者动态加载
- 启动项可选异步import【非必须要求】,见so异步加载
因为lazy-import不支持系统库,所以针对系统库的导入可以通过以下两种方式来优化:
- 将系统库/三方库封装到统一的LazyTasks文件中执行具体方法,业务中lazy-import LazyTasks文件,间接实现延迟加载
动态加载,统一在DynamicTask文件动态import库,业务用到时导入DynamicTask执行具体任务【推荐】
3、缩短Ability生命周期阶段耗时
避免在Ability生命周期回调接口进行耗时操作 非启动必须task延后执行: 启动任务非必要不添加在Ability内 新建首页渲染完成 DrawCompleteTask:在首页渲染完成后执行,可以将启动耗时任务移到这里
4、缩短加载绘制首页阶段耗时
- 将网络请求提前至AbilityStage/UIAbility生命的onCreate()生命周期回调函数中,将首刷或二刷的时间提前,减少用户等待时间
- 使用本地缓存首页数据
- 减少组件嵌套层级:
- 布局嵌套层次过深会增加创建节点和布局的时间,应避免冗余嵌套,尽量使用扁平化布局优化层级。
- 网络请求较少JSON.stringify次数等
- LazyForEach懒加载
- 使用LazyForEach懒加载替换ForEach,避免一次性初始化和加载所有元素,从而减少首帧绘制时创建列表元素的时间,提升响应性能。
- 代码逻辑优化
- 代码逻辑的优劣显著影响应用响应速度,自定义组件的生命周期变更会调用相应的回调函数,aboutToAppear函数会在创建自定义组件实例后,页面绘制之前执行,而onPageShow则是在页面进入前台的时候显示,因此避免在这两个回调函数中执行该耗时操作,不阻塞页面绘制。优化代码、减少冗余、避免耗时操作,可以提升执行效率。
- 页面及自定义组件的生命周期流程
- 异步任务并发处理:
- 多线程并发能力实现方式
- 利用TaskPool执行简单并行任务,避免阻塞主线程,提升响应速度。
- 利用Worker完成周期类耗时操作,避免TaskPool频繁拉起影响性能。
- TaskPool与Worker两种多线程并发能力均是基于 Actor并发模型实现的。Worker主、子线程通过收发消息进行通信;TaskPool基于Worker做了更多场景化的功能封装,例如支持任务组TaskGroup、任务优先级设置、取消任务等功能,且可以根据任务数量进行自动的扩容与缩容,还可以根据任务优先级进行任务调度。
- 耗时任务并发场景
- 耗时任务是指需要较长时间执行的任务,如果在UI主线程执行,可能导致应用卡顿、掉帧或响应延迟。典型的耗时任务包括CPU密集型任务、I/O密集型任务和同步任务,比如JSON解析、数据库操作、图片/视频编解码、压缩/解压缩等
- 多线程并发能力实现方式
- 异步任务并发处理:
更多关于HarmonyOS 鸿蒙Next中APP冷启动优化探索的实战教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙Next中APP冷启动优化主要涉及以下技术点:
- 应用包管理框架优化,采用HAP包分片加载机制;
- 方舟编译器静态编译生成高效机器码;
- 预置资源索引表加速资源加载;
- 使用分布式调度实现任务并行初始化;
- 内存预分配技术减少运行时开销。
关键指标包括启动时间控制在400ms内,首帧渲染完成时间低于500ms。可通过Trace工具抓取启动各阶段耗时,重点关注UI线程和主线程阻塞问题。
更多关于HarmonyOS 鸿蒙Next中APP冷启动优化探索的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
针对HarmonyOS Next应用冷启动优化,以下关键点值得重点关注:
- 启动阶段分析:
- 重点关注Application/Ability初始化阶段(占冷启动耗时40%以上)
- 使用Profiler的Launch分析工具定位具体瓶颈
- 核心优化手段: (1)资源加载优化:
- 严格控制启动页图标尺寸(≤256x256px)
- 使用Lazy-Import延迟非必要模块加载
- 避免多层export *嵌套导出
(2)代码结构优化:
- 拆分HAR包的index.ets为冷热启动两个版本
- 对系统库采用动态import或封装延迟加载
- 减少首屏组件嵌套层级(建议≤5层)
- 性能检测:
- 通过TeslaLab获取准确启动耗时数据
- 使用ArkTS Callstack分析函数热点
- 监控extend_time指标确保≤1500ms
- 线程优化:
- 耗时任务使用TaskPool并发处理
- 网络请求提前到AbilityStage.onCreate
- 避免在aboutToAppear执行耗时操作
典型优化案例: 某应用通过以下改动降低冷启动耗时35%:
- 将38个import改为Lazy-Import
- 拆分HAR导出文件
- 首屏改用LazyForEach
- 系统库改为动态加载
建议优先处理:
- 冗余import清理
- 首屏布局简化
- 非必要初始化延迟
- 大数据请求分片加载