页面生命周期与 UIAbility 生命周期如何配合?页面销毁是否一定意味着 Ability 销毁?一个 Ability 内多个页面如何管理资源?
在 ArkTS 的架构中,UIAbility 是“容器”,而 页面(Page) 是“内容”。理解它们的嵌套关系和生命周期步调,是解决应用闪退、掉帧和内存增长的关键。
1. 页面与 UIAbility 生命周期的配合
它们的关系类似于“大盒子”与“小盒子”。当大盒子移动时,里面的小盒子必然随之移动。
典型的配合流程(以冷启动为例):
- Ability 启动: 执行
onCreate。 - 加载内容:
onWindowStageCreate调用windowStage.loadContent('pages/Index')。 - 页面初始化: 页面组件触发
aboutToAppear。 - Ability 变为可见: 执行
onForeground。 - 页面变为可见: 页面触发
onPageShow。
生命周期对照表:
| 动作 | UIAbility 生命周期 | 页面 (Page) 生命周期 |
|---|---|---|
| 应用进入后台 | onBackground | onPageHide |
| 从后台切回前台 | onForeground | onPageShow |
| 用户点击返回键退出页面 | (不触发,除非是最后一个页面) | onBackPress aboutToDisappear |
| 系统销毁 Ability 实例 | onDestroy | aboutToDisappear |
2. 页面销毁是否一定意味着 Ability 销毁?
结论:不一定。
这取决于当前的**路由栈(Router Stack)**状态:
- 场景 A(非销毁): 当你从
PageA跳转到PageB(使用router.pushUrl)时,PageA仅触发onPageHide,它并没有销毁,而是被压入栈底。此时 UIAbility 稳如泰山。 - 场景 B(页面销毁但 Ability 存活): 当你从
PageB返回PageA(调用router.back)时,PageB会触发aboutToDisappear并被销毁,但 UIAbility 依然存活,因为它还在展示PageA。 - 场景 C(共同销毁): 当你在栈底页面(主页)点击返回键时,页面执行销毁,由于没有更多页面可显示,UIAbility 也会随之进入
onWindowStageDestroy和onDestroy。
3. 一个 Ability 内多个页面的资源管理
在单 Ability 多页面(Single Ability Multi-Page)架构下,资源管理需要极度精细,否则随着跳转次数增加,内存会迅速“爆表”。
A. 巧用 LocalStorage 进行物理隔离
不要把所有页面的状态都塞进全局的 AppStorage。
- 实践: 在 Ability 加载时创建一个
LocalStorage实例。 - 好处: 页面可以通过
@LocalStorageLink共享数据,而当 UIAbility 销毁时,这个LocalStorage会被整体回收,干净利落。
B. 区分“可见性释放”与“彻底销毁释放”
onPageHide阶段: 适合释放高频刷新的资源。例如:停止页面上的计时器、暂停视频播放、取消高频传感器订阅。aboutToDisappear阶段: 适合释放内存占用大的资源。例如:置空大的图片数组、解绑全局事件监听(Emitter)、销毁自定义的后台 Worker。
C. 路由缓存机制与页面限制
- 栈深度限制: 页面栈最大深度为 32。达到上限后无法 push。
- 清栈操作: 对于登录页、引导页等“一去不复返”的页面,跳转时应使用
router.replaceUrl而不是pushUrl。这样旧页面会立即触发aboutToDisappear释放资源,而不是留在栈底吃内存。
总结
- Ability 是环境,Page 是状态:环境不可见时(
onBackground),所有 Page 都会隐藏(onPageHide)。 - 生命周期成对原则:在
aboutToAppear申请的非 UI 资源(如长连接),必须在aboutToDisappear手动断开。 - 按需生存:非主页业务尽量使用
router.replace或在onPageHide时主动清理不再需要的缓存大图。