文章配图:如何管理启动参数、初始化全局状态和预加载配置

页面预览

前言

在 HarmonyOS 应用开发中,每个应用都有一个或多个 Ability 作为入口。其中 UIAbility 是包含 UI 界面的应用组件,负责管理应用窗口、生命周期和页面路由——它是整个应用的"大门"。

本文以「猫猫大作战」的 EntryAbility.ets 源码为主线,拆解 UIAbility 的完整生命周期、在 module.json5 中的注册方式,以及 onWindowStageCreateloadContent 的首屏加载链路,带你吃透 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
应用退出:onDestroy

2.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/ 目录。exportedtrue 时,其他应用可通过 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 onForeground

7.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 生命周期:onCreateonWindowStageCreateonForeground / onBackgroundonDestroy
  • loadContent('pages/Index') 负责加载首屏页面
  • module.json5 中必须注册 Ability 才能被系统识别
  • onBackground 不允许做耗时操作
  • hilog 替代 console.log 进行日志输出

下一篇预告:第 72 篇将深入 onCreate 冷启动初始化,讲解如何管理启动参数、初始化全局状态和预加载配置。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐