在 ArkTS 的 Stage 模型中,生命周期的划分核心在于**“资源消耗与用户感知的平衡”**。如果放错了地方,轻则导致应用启动卡顿,重则引发内存泄漏或系统强杀。
以下是针对你问题的深度解析:
1. 生命周期阶段的“最佳实践”分配
| 阶段 | 适合的操作 | 为什么? |
|---|---|---|
onCreate | 全局非 UI 逻辑初始化:初始化数据库、设置全局变量、订阅全局事件(如 Emitter)、读取持久化配置文件。 | 仅执行一次,适合建立应用的基础运行环境。 |
onWindowStageCreate | UI 环境绑定:加载 Index 页面、设置窗口沉浸式、监听窗口尺寸变化。 | 此时 windowStage 实例才可用,是 UI 渲染的起点。 |
onForeground | 交互状态激活:恢复动画、开启传感器监听(如陀螺仪)、刷新列表实时数据、请求焦点。 | 用户刚看到界面,适合开启高频或高耗电的交互任务。 |
onBackground | 资源释放与保存:停止动画、关闭高耗能传感器、保存草稿数据。 | 应用不可见,通过释放非必要资源降低系统内存压力,防止被系统杀掉。 |
onDestroy | 彻底清理:取消全局订阅、手动置空大对象、关闭数据库连接。 | 实例彻底退出,进行最后的扫尾工作。 |
2. 为什么不建议在 onForeground 做“重初始化”?
很多开发者习惯在 onForeground 重新请求所有网络数据或重新实例化对象,这会导致以下问题:
- 用户感知卡顿(Jank):
onForeground是在界面显示前的最后一步。如果你在此执行耗时操作(如同步解析大 JSON、同步读写文件),UI 线程会被阻塞,用户会感觉到明显的“转场掉帧”或“白屏”。 - 冗余操作: 热启动时也会触发
onForeground。如果数据本身已经在内存中且有效,重复初始化会浪费 CPU 和电量。 - 状态丢失: 如果你在
onForeground强行重置数据模型,用户之前在界面上的临时操作(如输入一半的文本、滚动的进度)可能会被意外覆盖。
正确做法: * 按需刷新:对比时间戳或数据指纹,仅在数据过期时刷新。
- 异步处理:利用
async/await将耗时初始化放在后台处理,UI 先显示缓存内容。
3. 如何避免内存泄漏?
在 ArkTS 中,虽然有 GC(垃圾回收),但由于闭包、全局引用和事件订阅的存在,内存泄漏依然常见。以下是三条“军规”:
A. 订阅必须成对出现(Lifecycle-Pairing)
这是最常见的泄漏点。如果你在 onCreate 或 aboutToAppear 中订阅了某个全局事件,必须在对应的销毁阶段取消。
onCreateonDestroy(UIAbility)onForegroundonBackground(临时监听)aboutToAppearaboutToDisappear(自定义组件)
B. 慎用全局闭包与单例引用
如果你在单例(Singleton)中存储了一个回调函数,且这个函数里引用了 UI 层的 this(组件实例):
- 后果:即使组件被销毁,单例依然持有着对该组件的引用,导致整棵 UI 树无法回收。
- 对策:在组件销毁前,务必将单例中的回调置为
null。
C. 合理管理 AppStorage 与 LocalStorage
- 问题:向
AppStorage写入大对象(如位图、大数组)后,如果没有手动删除,它会随应用生命周期常驻内存。 - 对策:对于局部页面使用的数据,优先使用
LocalStorage(随页面销毁自动回收),或在业务结束时调用AppStorage.delete()。
总结:架构师的“金句”
onCreate定根基:初始化一定要快,别挡着 UI 显示。onForeground重恢复:只做激活,不做重造。onBackground懂舍弃:能关的传感器都关掉,给系统省点电,应用才不容易被杀。onDestroy扫尾净:订阅不取消,内存跑不了。