HarmonyOS Stage 模型实战:UIAbility 生命周期如何驱动页面安全状态

为什么一个页面需要两套生命周期

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() 每次都注册新的匿名函数,可能出现:

  • 同一事件触发多次;
  • 已销毁组件仍被闭包引用;
  • 重复发起系统认证;
  • 问题只在多次导航后出现。

更稳妥的做法是:

  1. 把回调保存为稳定成员;
  2. 确认页面在当前导航结构中是否只创建一次;
  3. 在对应消失/销毁阶段调用 off
  4. 或把订阅放在更稳定的应用级对象中。

示意:

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 下载华为应用市场下载

参考资料


相关推荐
m0_749690231 小时前
【寻迹校园 HarmonyOS NEXT 实战 28】不交换手机号也能交接:固定校内交接点的隐私设计
华为·harmonyos·arkts·产品设计·隐私设计·安全交接
贾伟康2 小时前
【中国方言题库|09】HarmonyOS ArkTS 方言搜索实战:实现词语检索和无结果反馈
harmonyos·arkts·状态管理·arkui·本地搜索
Sagittarius_A*2 小时前
【好靶场】PHP反序列化入门练习
安全·php·web·反序列化
梦想不只是梦与想2 小时前
鸿蒙 AppGallery Connect:查看应用信息(三)
harmonyos·appgallery·client id·app id·developer id
贾伟康3 小时前
【中国方言题库|03】HarmonyOS ArkTS 四川话分库实战:复用题库组件并保持地区参数清晰
harmonyos·arkts·arkui·路由传参·组件复用
IT大白鼠3 小时前
Kubernetes Goat:云原生安全靶场实践指南
安全·云原生·kubernetes
zlinear数据采集卡3 小时前
数据采集卡从入门到精通(42):项目实战三——设备预测性维护系统,从布点到预警
开发语言·单片机·嵌入式硬件·安全·fpga开发
贾伟康3 小时前
【中国方言题库|10】HarmonyOS ArkTS 语音播放实战:管理读音播放与页面生命周期
生命周期·harmonyos·arkts·语音合成·texttospeech
sbjdhjd3 小时前
CTF 技术复盘:从参数类型绕过到正则回溯 | Merry Christmas PHP CTF(gift.php & gift_plus.php)
安全·网络安全·云计算·php·ctf·红队·网络攻防