HarmonyOS应用开发实战:猫猫大作战-如何管理启动参数、初始化全局状态和预加载配置
前言
在 HarmonyOS 应用开发中,每个应用都有一个或多个Ability作为入口。其中UIAbility是包含 UI 界面的应用组件,负责管理应用窗口、生命周期和页面路由——它是整个应用的“大门“。
本文以「猫猫大作战」的EntryAbility.ets源码为主线,拆解 UIAbility 的完整生命周期、在module.json5中的注册方式,以及onWindowStageCreate→loadContent的首屏加载链路,带你吃透 HarmonyOS 应用入口的核心机制。
提示:本系列不讲 ArkTS 基础语法与环境搭建,假设你已跟完第 1–70 篇。本篇是阶段三第 71 篇。
一、项目中的 EntryAbility
1.1 源码定位
打开「猫猫大作战」项目entry/src/main/ets/entryability/EntryAbility.ets,可以看到最精简的入口实现:
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit'; import { window } from '@kit.ArkUI'; import { hilog } from '@kit.PerformanceAnalysisKit'; const TAG: string = 'EntryAbility'; const DOMAIN: number = 0xFF00; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { hilog.info(DOMAIN, TAG, '%{public}s', 'Ability onCreate'); } onDestroy() { hilog.info(DOMAIN, TAG, '%{public}s', 'Ability onDestroy'); } onWindowStageCreate(windowStage: window.WindowStage) { hilog.info(DOMAIN, TAG, '%{public}s', 'Ability onWindowStageCreate'); windowStage.loadContent('pages/Index', (err) => { if (err.code) { hilog.error(DOMAIN, TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; } hilog.info(DOMAIN, TAG, '%{public}s', 'Succeeded in loading the content.'); }); } onWindowStageDestroy() { hilog.info(DOMAIN, TAG, '%{public}s', 'Ability onWindowStageDestroy'); } onForeground() { hilog.info(DOMAIN, TAG, '%{public}s', 'Ability onForeground'); } onBackground() { hilog.info(DOMAIN, TAG, '%{public}s', 'Ability onBackground'); } }1.2 结构解析
| 元素 | 说明 | 来源 |
|---|---|---|
UIAbility | 基类,提供生命周期回调 | @kit.AbilityKit |
Want | 启动参数,携带目标、数据 | @kit.AbilityKit |
AbilityConstant | 启动原因常量 | @kit.AbilityKit |
WindowStage | 窗口阶段,承载 UI 加载 | @kit.ArkUI |
hilog | 系统日志工具 | @kit.PerformanceAnalysisKit |
二、UIAbility 生命周期全景
2.1 六个核心回调
UIAbility 的生命周期包含6 个回调,调用顺序如下:
冷启动:onCreate → onWindowStageCreate → onForeground ↓ loadContent('pages/Index') ↓ Index 页面渲染(aboutToAppear → build → onDidBuild) ↓ 切后台:onBackground 切前台:onNewWant → onForeground ↓ 窗口销毁:onWindowStageWillDestroy → onWindowStageDestroy 应用退出:onDestroy2.2 各回调用途速查
| 回调 | 触发时机 | 项目用途 |
|---|---|---|
onCreate | 首次创建 Ability 实例 | 初始化全局配置、日志 |
onWindowStageCreate | WindowStage 创建完成后 | 加载首屏页面、订阅窗口事件 |
onForeground | Ability 进入前台 | 恢复游戏/定位等资源 |
onBackground | Ability 完全不可见 | 暂停游戏、释放资源 |
onWindowStageDestroy | WindowStage 销毁后 | 释放 WindowStage 资源 |
onDestroy | Ability 实例销毁前 | 保存数据、释放全局资源 |
2.3 各回调生命周期表
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit'; import { window } from '@kit.ArkUI'; import { hilog } from '@kit.PerformanceAnalysisKit'; const TAG = 'EntryAbility'; const DOMAIN = 0xFF00; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 冷启动时初始化一次:读取配置、初始化日志 hilog.info(DOMAIN, TAG, 'onCreate called'); // 此处可初始化全局数据,如 AppStorage 预置默认值 AppStorage.setOrCreate('highScore', 0); } onWindowStageCreate(windowStage: window.WindowStage): void { // WindowStage 创建后加载首屏 hilog.info(DOMAIN, TAG, 'onWindowStageCreate called'); // 订阅 WindowStage 事件(获焦/失焦) windowStage.on('windowStageEvent', (data) => { const eventType: window.WindowStageEventType = data; switch (eventType) { case window.WindowStageEventType.ACTIVE: hilog.info(DOMAIN, TAG, '窗口获焦'); break; case window.WindowStageEventType.INACTIVE: hilog.info(DOMAIN, TAG, '窗口失焦'); break; case window.WindowStageEventType.HIDDEN: hilog.info(DOMAIN, TAG, '窗口隐藏'); break; default: break; } }); // 加载首屏页面 windowStage.loadContent('pages/Index', (err) => { if (err.code) { hilog.error(DOMAIN, TAG, '加载页面失败: %{public}s', JSON.stringify(err)); return; } hilog.info(DOMAIN, TAG, '页面加载成功'); }); } onForeground(): void { hilog.info(DOMAIN, TAG, 'onForeground called'); // 回到前台:可恢复游戏引擎 } onBackground(): void { hilog.info(DOMAIN, TAG, 'onBackground called'); // 切到后台:可暂停游戏 } onWindowStageDestroy(): void { hilog.info(DOMAIN, TAG, 'onWindowStageDestroy called'); } onDestroy(): void { hilog.info(DOMAIN, TAG, 'onDestroy called'); // 保存高分到持久化存储 } }三、module.json5 中的注册
3.1 配置结构
UIAbility 必须在module.json5中注册才能被系统识别。猫猫大作战的entry/src/main/module.json5:
{ "module": { "name": "entry", "type": "entry", "deviceTypes": ["tablet", "phone", "wearable"], "deliveryWithInstall": true, "installationFree": false, "pages": "$profile:main_pages", "abilities": [ { "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", "description": "$string:EntryAbility_desc", "icon": "$media:app_icon", "label": "$string:EntryAbility_label", "startWindowIcon": "$media:app_icon", "startWindowBackground": "$color:start_window_background", "exported": true, "skills": [ { "actions": ["action.system.home"] } ] } ] } }3.2 关键字段说明
| 字段 | 值 | 含义 |
|---|---|---|
name | EntryAbility | Ability 名称,唯一标识 |
srcEntry | ./ets/entryability/EntryAbility.ets | 源码路径 |
exported | true | 允许其他应用通过 Want 启动 |
skills[0].actions | action.system.home | 桌面图标入口 |
startWindowIcon | $media:app_icon | 启动窗口图标 |
startWindowBackground | $color:start_window_background | 启动窗口背景色 |
注意:
srcEntry路径相对于entry/src/main/目录。exported为true时,其他应用可通过startAbility启动你的 Ability。
四、启动流程时序
4.1 冷启动时序
用户点击桌面图标 ↓ Launcher 构造 Want → action.system.home ↓ AAFWK(Ability Manager Service)创建 UIAbility 实例 ↓ onCreate(want, launchParam) ← ① 第一次初始化 ↓ onWindowStageCreate(windowStage) ← ② 窗口创建 ↓ windowStage.loadContent('pages/Index') ← ③ 加载页面 ↓ Index.ets aboutToAppear → build → onDidBuild ← ④ 页面渲染 ↓ onForeground() ← ⑤ 进入前台 ↓ 用户可见、可交互4.2 热启动(从后台回到前台)
用户从最近任务/桌面重新打开 ↓ onNewWant(want, launchParam) ← ① 携带新参数 ↓ onForeground() ← ② 进入前台 ↓ onPageShow() ← ③ Index 页面级回调五、Want 与启动参数
5.1 Want 结构
onCreate中接收的Want参数包含了启动的完整信息:
// Want 的核心字段 interface Want { deviceId?: string; // 目标设备 ID(跨设备时使用) bundleName?: string; // 目标应用 Bundle 名称 abilityName?: string; // 目标 Ability 名称 uri?: string; // 通用资源标识符 type?: string; // MIME 类型 parameters?: Record<string, Object>; // 自定义参数 flags?: number; // 启动标记 }5.2 在 EntryAbility 中解析启动参数
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 解析启动原因 switch (launchParam.launchReason) { case AbilityConstant.LaunchReason.START_ABILITY: hilog.info(DOMAIN, TAG, '通过 startAbility 启动'); break; case AbilityConstant.LaunchReason.CALL: hilog.info(DOMAIN, TAG, '通过 call 启动'); break; case AbilityConstant.LaunchReason.CONTINUATION: hilog.info(DOMAIN, TAG, '通过跨端迁移启动'); break; case AbilityConstant.LaunchReason.APP_RECOVERY: hilog.info(DOMAIN, TAG, '通过应用恢复启动'); break; default: break; } // 解析自定义参数(比如通过 DeepLink 启动) const targetPage = want.parameters?.['targetPage'] as string; if (targetPage) { AppStorage.setOrCreate('targetPage', targetPage); } }六、常见踩坑
6.1 坑一:loadContent 路径错误
// 🚫 错误:路径多了 src/ windowStage.loadContent('src/main/ets/pages/Index', ...); // ✅ 正确:路径相对于 entry/src/main/ets/ windowStage.loadContent('pages/Index', (err) => { if (err.code) { hilog.error(DOMAIN, TAG, '加载失败: %{public}s', JSON.stringify(err)); } });6.2 坑二:忘记在 module.json5 注册
// 🚫 错误:只写了文件,没在 abilities 数组中注册 { "module": { "abilities": [] // ❌ 空的,系统找不到 EntryAbility } }表现:应用启动直接闪退,报Cannot find ability。
6.3 坑三:onBackground 中做耗时操作
// 🚫 错误:onBackground 中执行数据库写入等耗时操作 onBackground(): void { this.saveGameData(); // ❌ 可能耗时超过系统限制 } // ✅ 正确:在 onPageHide 或异步任务中处理 onBackground(): void { // 仅标记状态,不做耗时操作 AppStorage.set('isBackground', true); }
onBackground()执行时间极短,不适合做数据库事务、网络请求等操作。
七、HiLog 日志验证
7.1 日志输出
在 DevEco Studio 中运行应用并过滤EntryAbility,可以看到完整的生命周期调用链:
09:15:23.101 [INFO] EntryAbility: Ability onCreate 09:15:23.156 [INFO] EntryAbility: Ability onWindowStageCreate 09:15:23.201 [INFO] EntryAbility: Succeeded in loading the content. 09:15:23.225 [INFO] Index: Index aboutToAppear called 09:15:23.289 [INFO] Index: Index onDidBuild called 09:15:23.302 [INFO] EntryAbility: Ability onForeground7.2 使用 hilog 替代 console
在EntryAbility.ets中,官方推荐使用hilog而非console.log:
| 方面 | console | hilog |
|---|---|---|
| 性能 | 未优化 | 异步写入,不阻塞主线程 |
| 分级 | 无 | DEBUG/INFO/WARN/ERROR/FATAL |
| 过滤 | 按字符串 | 按 DOMAIN + TAG |
| 线上采集 | 不支持 | 支持 HiAppEvent 采集 |
// 推荐:在 EntryAbility 中使用 hilog import { hilog } from '@kit.PerformanceAnalysisKit'; hilog.info(0xFF00, 'EntryAbility', '应用启动完成');八、总结
EntryAbility是 HarmonyOS 应用的起点,承载了冷启动初始化、窗口创建、首屏加载和前后台切换等核心职责。本文从「猫猫大作战」的实际源码出发,覆盖了 UIAbility 的六大生命周期回调、module.json5 注册规范、Want 参数解析和常见踩坑。
核心要点:
- UIAbility 生命周期:
onCreate→onWindowStageCreate→onForeground/onBackground→onDestroy loadContent('pages/Index')负责加载首屏页面module.json5中必须注册 Ability 才能被系统识别onBackground不允许做耗时操作- 用
hilog替代console.log进行日志输出
下一篇预告:第 72 篇将深入onCreate冷启动初始化,讲解如何管理启动参数、初始化全局状态和预加载配置。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- UIAbility 组件生命周期官方文档
- UIAbility 概述
- UIAbility 使用指导
- module.json5 配置参考
- Want 与启动参数文档
- 开源鸿蒙跨平台社区
- 第 72 篇:onCreate 冷启动初始化
- 第 73 篇:loadContent 首屏绑定
