为什么一个页面需要两套生命周期
HarmonyOS Stage 模型中,UIAbility 是带界面的应用组件,WindowStage 提供窗口绘制区域,ArkUI 页面则管理实际组件树。
这意味着我们会同时遇到:
- UIAbility 生命周期;
- WindowStage 生命周期;
- ArkUI 自定义组件生命周期。
它们不是重复设计,而是负责不同层级。
在"心晴手记"中:
EntryAbility负责加载页面和转发前后台事件;Index页面负责读取本地状态、决定隐私门禁和应用锁 UI;- Repository 和服务层负责具体系统能力。
一、Stage 模型中的基本分工
官方对 Stage 模型的核心角色划分为:
text
AbilityStage:Module 级组件管理器
UIAbility:包含 UI 的应用组件
WindowStage:UIAbility 对应的窗口舞台
ArkUI Page/Component:实际界面与交互状态
Context:访问资源和系统能力
对一个单入口 App 来说,不必为了"架构完整"把所有回调都填满。重要的是把代码放到正确层级。
二、onWindowStageCreate 只负责装载页面
项目的 EntryAbility 很精简:
typescript
import {
AbilityConstant,
UIAbility,
Want
} from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onCreate(
_want: Want,
_launchParam: AbilityConstant.LaunchParam
): void {
}
onWindowStageCreate(
windowStage: window.WindowStage
): void {
windowStage.loadContent('pages/Index');
}
}
这里没有在 Ability 中直接读取心情记录、创建习惯或操作页面组件。
原因是:
- UIAbility 应知道加载哪个页面;
- 页面更了解自身需要哪些数据;
- Repository 初始化需要页面使用的 UIAbilityContext;
- 业务状态放进 Ability 会增加 UI 同步难度。
如果多个页面共享大量初始化结果,可以再考虑 Module 级对象、状态管理或依赖注入,而不是先把所有业务塞进 onCreate()。
三、aboutToAppear 适合页面级状态恢复
页面出现时完成:
typescript
async aboutToAppear(): Promise<void> {
await appRepository.initialize(this.context);
const state: PersistedState = await appRepository.load();
this.applyLanguagePreference(
state.settings.languagePreference
);
this.settings = state.settings;
this.moodEntries = state.moodEntries;
this.habits = state.habits;
this.completions = state.habitCompletions;
this.loadTodayDraft();
this.loaded = true;
}
这个顺序表达了一个清楚的门槛:
text
Repository 初始化
→ 加载磁盘状态
→ 应用语言偏好
→ 写入页面状态
→ 恢复今日草稿
→ loaded = true
在 loaded 之前,页面不应该假设数据已经可用,也不应该启动依赖设置的交互。
四、Ability 前后台事件为什么要转发给页面
应用进入后台和回到前台,是 UIAbility 能直接感知的生命周期:
typescript
onForeground(): void {
this.context.eventHub.emit('moodMemoirForeground');
}
onBackground(): void {
this.context.eventHub.emit('moodMemoirBackground');
}
但"回前台时是否锁定"取决于页面设置 appLockEnabled,同时锁定遮罩也是页面状态。
因此 Ability 不直接调用页面方法,而是通过 eventHub 发布语义事件:
text
moodMemoirForeground
moodMemoirBackground
页面订阅:
typescript
this.context.eventHub.on(
'moodMemoirBackground',
() => {
if (this.settings.appLockEnabled) {
this.locked = true;
}
}
);
typescript
this.context.eventHub.on(
'moodMemoirForeground',
() => {
if (
this.settings.appLockEnabled &&
this.loaded
) {
this.locked = true;
this.unlock();
}
}
);
它形成了一个小型边界:Ability 报告系统事实,页面决定业务响应。
五、为什么后台时先把 locked 设为 true
应用锁场景中,顺序影响隐私。
错误流程可能是:
text
回到前台
→ 先显示旧主页面
→ 再弹系统认证
即使只有几百毫秒,日记内容也可能闪现。
更稳妥的流程是:
text
进入后台
→ 立即把 locked 设为 true
→ 回到前台仍显示锁定层
→ 发起系统认证
→ 成功后才解除锁定
因此 onBackground 事件不是为了在后台执行任务,而是提前调整下一次可见状态。
六、冷启动和回前台是两条不同路径
冷启动时还没有后台事件,因此页面加载完设置后也要检查:
typescript
if (this.settings.appLockEnabled) {
this.locked = true;
this.unlock();
}
完整逻辑需要覆盖:
| 场景 | 入口 |
|---|---|
| 第一次安装 | 页面加载 + 隐私门禁 |
| 普通冷启动 | 页面加载 + 设置恢复 |
| 开启应用锁的冷启动 | 页面加载 + 首次认证 |
| 后台回前台 | UIAbility 事件 + 再认证 |
| 认证失败 | 保持锁定页 |
| 关闭应用锁 | 更新设置并解除锁定 |
只处理 onForeground() 会漏掉冷启动,只处理页面出现又会漏掉应用仍在同一页面但从后台恢复的情况。
七、eventHub 监听要考虑解除注册
事件总线很方便,但也容易产生隐蔽问题。
如果组件被销毁后重新创建,而 aboutToAppear() 每次都注册新的匿名函数,可能出现:
- 同一事件触发多次;
- 已销毁组件仍被闭包引用;
- 重复发起系统认证;
- 问题只在多次导航后出现。
更稳妥的做法是:
- 把回调保存为稳定成员;
- 确认页面在当前导航结构中是否只创建一次;
- 在对应消失/销毁阶段调用
off; - 或把订阅放在更稳定的应用级对象中。
示意:
typescript
private foregroundHandler = (): void => {
// 处理前台事件
};
aboutToAppear(): void {
this.context.eventHub.on(
'moodMemoirForeground',
this.foregroundHandler
);
}
aboutToDisappear(): void {
this.context.eventHub.off(
'moodMemoirForeground',
this.foregroundHandler
);
}
具体生命周期 API 应以当前 ArkUI 组件模型为准,但"谁注册、谁释放"这个原则不会变化。
八、不要把 onBackground 当成后台常驻能力
Stage 模型对后台行为有系统治理。onBackground() 表示组件进入后台,不意味着应用可以从此任意常驻并持续执行任务。
如果需要提醒、长时任务或特定后台能力,应使用对应的 HarmonyOS 系统机制,并满足权限与场景约束。
本项目没有后台服务。日记、习惯和统计都在前台完成,本地提醒也没有为了"功能对齐"而绕开系统开放能力要求。
这也是生命周期设计的重要边界:
text
生命周期回调 ≠ 后台任务调度器
九、建议的生命周期测试矩阵
页面初始化
- 首次安装无状态;
- 已有状态正常恢复;
- Preferences 内容损坏时回退;
- 语言偏好在完整渲染前应用。
前后台
- 主页面进入后台再返回;
- 系统认证弹窗期间再次切后台;
- 快速连续前后台切换;
- 应用锁关闭时不触发认证。
进程重建
- 杀进程后冷启动;
- 系统回收后恢复;
- 语言切换调用重启;
- 更新版本后加载旧状态。
监听管理
- 页面重复创建不会累积监听;
- 一个前台事件只触发一次业务处理;
- 页面销毁后没有遗留引用。
总结
Stage 模型中的职责可以简化为:
- UIAbility 感知应用组件和窗口生命周期;
- WindowStage 装载 ArkUI 页面;
- 页面恢复业务状态并决定显示;
eventHub传递系统事件,不直接暴露页面实现;- 敏感页面在后台时提前进入安全状态;
- 监听注册必须有明确生命周期;
- 后台回调不等于后台常驻能力。
理解这些边界后,生命周期代码会更少,也更容易验证。
本文案例来自"心晴手记(MoodMemoir)"HarmonyOS 版的 Stage 模型与应用锁状态联动。
体验心晴手记:App Store 下载 | 华为应用市场下载