鸿蒙应用开发实战【06】— 应用入口与生命周期管理(EntryAbility 深度解析)

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
在这里插入图片描述

HarmonyOS NEXT 取消了 Activity / ViewController 的概念,使用 UIAbility 作为应用的运行载体。每个 UIAbility 实例拥有独立的窗口、UI 栈和生命周期,是应用交互的基本单元。号码助手只有一个 UIAbility——EntryAbility,它承担着初始化数据库、加载首页、处理系统事件三大职责。

本篇深入讲解 EntryAbility 的完整实现,以及 UIAbility 各生命周期钩子的使用场景。

本篇涵盖:UIAbility 生命周期全图、EntryAbility 完整实现、onWindowStageCreate 窗口加载、DatabaseService 异步初始化时序、onForeground / onBackground 资源管理。


一、UIAbility 是什么

1.1 HarmonyOS 应用模型演进

版本 模型 特点
HarmonyOS 2.x FA 模型(Feature Ability) 类 Android Activity,已废弃
HarmonyOS 3.x+ Stage 模型(UIAbility) 面向多端、窗口化,当前主流
HarmonyOS NEXT Stage 模型(纯 ArkTS) 无 Android 兼容层,性能更优

Stage 模型的核心设计:UIAbility 负责生命周期管理,Page(ArkUI 页面)负责 UI 渲染,两者职责分离。

1.2 号码助手的 Ability 结构

号码助手应用
└── EntryAbility(唯一 UIAbility)
    ├── WindowStage(窗口舞台)
    │   └── MainWindow(主窗口)
    │       └── ArkUI 页面栈
    │           ├── LoginPage(栈底)
    │           ├── HomePage
    │           ├── CardManagePage
    │           └── ...(最多 32 层)
    └── DatabaseService(后台服务,随 Ability 初始化)

1.3 UIAbility 与页面的关系

// EntryAbility 负责:
// 1. 创建窗口(WindowStage)
// 2. 加载第一个页面
// 3. 初始化全局服务(数据库)

// 页面(Page)负责:
// 1. 渲染 UI
// 2. 响应用户交互
// 3. 通过 router 管理页面栈

二、UIAbility 生命周期全图

2.1 完整生命周期图

UIAbility 生命周期图

图1:UIAbility 完整生命周期状态机 — Create → Foreground ↔ Background → Destroy

2.2 各钩子触发时机

钩子方法 触发时机 适合做什么
onCreate Ability 首次创建 初始化全局状态、注册系统监听
onWindowStageCreate 窗口舞台就绪 加载首页、初始化数据库
onWindowStageDestroy 窗口舞台销毁 清理窗口相关资源
onForeground 应用进入前台 恢复数据、刷新状态
onBackground 应用进入后台 暂停耗时任务、保存草稿
onDestroy Ability 销毁 释放所有资源
onNewWant 热启动(已存在实例) 处理新的启动参数
// 生命周期调用顺序示例(冷启动)
onCreate()onWindowStageCreate()[用户操作应用]onWindowStageDestroy()
onDestroy()

三、EntryAbility 完整实现解析

3.1 基础骨架

// entry/src/main/ets/EntryAbility.ets
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
import { window } from '@kit.ArkUI'
import { DatabaseService } from './features/data/DatabaseService'

const DOMAIN = 0x0000        // hilog 域值(自定义,16进制)
const TAG = '[EntryAbility]'  // 日志 TAG,便于过滤

export default class EntryAbility extends UIAbility {
  // ...
}

3.2 onCreate — 应用创建钩子

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  // want:启动参数(包含 action、uri、parameters 等)
  // launchParam:启动原因(NORMAL / CALL / SHARE 等)
  hilog.info(DOMAIN, TAG, 'onCreate, launchReason: %{public}d',
    launchParam.launchReason)

  // ✅ 适合:初始化全局配置、注册系统事件监听
  // ❌ 不适合:初始化数据库(context 可用,但窗口未就绪)
  // ❌ 不适合:操作 UI(窗口还没创建)
}

Want 参数说明:当应用被其他 App 通过 startAbility 唤起时,want 携带调用方传递的数据,可用于实现应用间跳转(如分享、扫码跳转等)。

3.3 onWindowStageCreate — 核心生命周期

onWindowStageCreate(windowStage: window.WindowStage): void {
  hilog.info(DOMAIN, TAG, 'onWindowStageCreate')

  // ── 步骤1:异步初始化数据库(不阻塞 UI)──
  // 数据库初始化需要 context(文件路径),在此处才能获取
  DatabaseService.init(this.context).then(() => {
    hilog.info(DOMAIN, TAG, '数据库初始化成功')
  }).catch((e: Error) => {
    hilog.error(DOMAIN, TAG, '数据库初始化失败: %{public}s', JSON.stringify(e))
    // 即使数据库失败,也不崩溃——各 DAO 调用会在 catch 中处理
  })

  // ── 步骤2:加载应用首页(同步)──
  windowStage.loadContent('pages/LoginPage', (err, data) => {
    if (err.code) {
      hilog.error(DOMAIN, TAG, '加载页面失败,code: %{public}d', err.code)
      return
    }
    hilog.info(DOMAIN, TAG, '首页加载成功')
  })
}

关键设计决策

  1. 数据库初始化使用 .then().catch() 而不是 await,原因:

    • onWindowStageCreate 本身不是 async 方法
    • 不需要等待 DB 就绪才显示 UI(页面有加载状态处理)
  2. 即使数据库初始化失败,应用仍然正常启动——各页面的 DAO 调用有独立的 try/catch

3.4 onForeground 与 onBackground

onForeground(): void {
  hilog.info(DOMAIN, TAG, 'onForeground')
  // 适用场景:
  // - 恢复后台暂停的网络请求
  // - 刷新可能已过期的数据(如云同步后的本地缓存)
  // - 重新连接蓝牙、传感器等硬件
}

onBackground(): void {
  hilog.info(DOMAIN, TAG, 'onBackground')
  // 适用场景:
  // - 暂停视频/音频播放
  // - 停止位置更新
  // - 保存用户未提交的表单草稿
  // 号码助手目前无特殊处理(本地应用,无后台任务)
}

3.5 onDestroy

onDestroy(): void | Promise<void> {
  hilog.info(DOMAIN, TAG, 'onDestroy')
  // 适用场景:
  // - 关闭数据库连接(relationalStore 会自动处理)
  // - 取消所有订阅
  // - 释放大对象(Bitmap、Buffer 等)
  // 号码助手无需特殊处理
}

四、DatabaseService 初始化时序分析

4.1 时序图

EntryAbility.onWindowStageCreate()
  │
  ├──[异步]── DatabaseService.init()
  │             │
  │             ├── relationalStore.getRdbStore()    ≈ 50-200ms
  │             ├── executeSql(CREATE TABLE cards)   ≈ 10-30ms
  │             ├── executeSql(CREATE TABLE ...)
  │             └── ✅ DB 就绪
  │
  └──[同步]── windowStage.loadContent('pages/LoginPage')
                │
                ├── LoginPage.aboutToAppear()         ← 此时 DB 可能未就绪
                ├── LoginPage.build()
                └── 用户点击进入首页
                      │
                      └── HomePage.onPageShow()       ← DB 大概率已就绪(≈300ms后)
                            └── CardDao.listAll()     ← 带 try/catch

4.2 为什么各页面必须有 try/catch

// LoginPage 进入首页时
onPageShow(): void {
  this.loadData()   // 如果 DB 未就绪,会抛异常
}

private async loadData(): Promise<void> {
  try {
    this.cards = await CardDao.listAll()  // ← DB 可能还没初始化完成
  } catch (e) {
    // ✅ catch 住了:页面显示空列表,不崩溃
    console.error('[HomePage] loadData:', JSON.stringify(e))
  } finally {
    this.loading = false
  }
}

五、module.json5 中的 Ability 配置

5.1 完整配置解析

// entry/src/main/module.json5
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",    // 主 Ability,应用启动入口
    "deviceTypes": ["phone"],          // 支持设备:仅手机
    "deliveryWithInstall": true,
    "installationFree": false,

    "pages": "$profile:main_pages",    // 指向页面路由配置文件

    "abilities": [{
      "name": "EntryAbility",
      "srcEntry": "./ets/EntryAbility.ets",
      "description": "$string:EntryAbility_desc",
      "icon": "$media:app_icon",
      "label": "$string:EntryAbility_label",
      "startWindowIcon": "$media:app_icon",
      "startWindowBackground": "$color:start_window_background",

      // 系统 skill:使应用出现在桌面和应用列表中
      "skills": [{
        "entities": ["entity.system.home"],
        "actions": ["action.system.home"]
      }]
    }]
  }
}

5.2 main_pages.json 页面路由表

// entry/src/main/resources/base/profile/main_pages.json
{
  "src": [
    "pages/LoginPage",
    "pages/HomePage",
    "pages/CardManagePage",
    "pages/AddCardPage",
    "pages/CardDetailPage",
    "pages/AddAppPage",
    "pages/AppDetailPage",
    "pages/SearchPage",
    "pages/MePage",
    "pages/PasteImportPage",
    "pages/RebindPage",
    "pages/StatusListPage",
    "pages/BackupPage",
    "pages/AboutPage",
    "pages/WizardPage"
  ]
}

注意:页面路由表中的路径是相对于 ets/ 的字符串路径。所有在 router.pushUrl 中使用的 url 必须在此列表中注册,否则运行时报「页面不存在」错误。


六、Want 参数处理:多入口支持

6.1 从桌面图标启动(普通冷启动)

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  // launchParam.launchReason === AbilityConstant.LaunchReason.NORMAL
  // want.action === 'action.system.home'
  // 正常走 onWindowStageCreate → LoginPage
}

6.2 从通知/分享启动(含参数)

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  // 处理外部传入的启动参数
  if (want.parameters) {
    const targetPage = want.parameters['targetPage'] as string
    if (targetPage) {
      // 将目标页面存入 AppStorage,供首页跳转
      AppStorage.setOrCreate('launchTargetPage', targetPage)
    }
  }
}

6.3 已在运行时热启动

onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  // 应用已在后台,用户再次点击图标或从通知点击进入
  // 此时 onCreate 不会再触发,onNewWant 代替
  hilog.info(DOMAIN, TAG, 'onNewWant')
  // 处理新的 want 参数,更新应用内导航
}

七、窗口管理:全屏与状态栏

7.1 沉浸式状态栏配置

onWindowStageCreate(windowStage: window.WindowStage): void {
  // 获取主窗口
  const mainWindow = windowStage.getMainWindowSync()

  // 设置窗口全屏(内容延伸到状态栏下方)
  mainWindow.setWindowLayoutFullScreen(true).then(() => {
    // 设置状态栏文字颜色为深色(适合浅色背景)
    const sysBarProps: window.SystemBarProperties = {
      statusBarColor: '#00000000',     // 透明状态栏
      statusBarContentColor: '#14141E' // 深色文字
    }
    mainWindow.setWindowSystemBarProperties(sysBarProps)
  })

  // 加载首页
  windowStage.loadContent('pages/LoginPage')
}

7.2 获取安全区域(避开刘海/圆角)

// 在页面中获取安全区域,避免内容被刘海遮挡
import { window } from '@kit.ArkUI'

// 使用 expandSafeArea 让组件自动适应安全区域
Column() {
  // 内容区域
}
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

八、EntryAbility 完整代码

// EntryAbility.ets — 号码助手完整入口实现
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
import { window } from '@kit.ArkUI'
import { DatabaseService } from './features/data/DatabaseService'

const DOMAIN = 0x0000
const TAG = '[EntryAbility]'

export default class EntryAbility extends UIAbility {
  /** 应用冷启动时调用一次 */
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(DOMAIN, TAG, 'onCreate, launchReason=%{public}d',
      launchParam.launchReason)
  }

  /** 窗口舞台创建完成 — 最重要的生命周期 */
  onWindowStageCreate(windowStage: window.WindowStage): void {
    hilog.info(DOMAIN, TAG, 'onWindowStageCreate')

    // 异步初始化数据库,不阻塞 UI
    DatabaseService.init(this.context)
      .then(() => hilog.info(DOMAIN, TAG, 'DB 初始化成功'))
      .catch((e: Error) => hilog.error(DOMAIN, TAG, 'DB 初始化失败: %{public}s', e.message))

    // 加载登录页(应用视觉入口)
    windowStage.loadContent('pages/LoginPage', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, TAG, 'loadContent 失败: %{public}d', err.code)
        return
      }
      hilog.info(DOMAIN, TAG, 'loadContent 成功')
    })
  }

  onWindowStageDestroy(): void {
    hilog.info(DOMAIN, TAG, 'onWindowStageDestroy')
  }

  onForeground(): void {
    hilog.info(DOMAIN, TAG, 'onForeground')
  }

  onBackground(): void {
    hilog.info(DOMAIN, TAG, 'onBackground')
  }

  onDestroy(): void {
    hilog.info(DOMAIN, TAG, 'onDestroy')
  }
}

九、常见问题与排查

9.1 数据库「尚未初始化」错误

现象:进入首页后 Toast 显示「加载失败」

原因onPageShow 触发时 DatabaseService.init() 尚未完成(通常在低端设备上 DB 初始化超过 300ms)

解决:各 DAO 方法的 catch 块正确处理,页面显示空状态而不是崩溃:

private async loadData(): Promise<void> {
  try {
    this.cards = await CardDao.listAll()
  } catch (e) {
    // 静默处理:DB 未就绪时显示空列表
    this.cards = []
  } finally {
    this.loading = false
  }
}

9.2 热启动时数据不刷新

现象:应用进入后台再回来,数据没有更新

解决:使用 onForegroundonPageShow 触发刷新:

// 页面级刷新:从子页面返回时刷新
onPageShow(): void {
  this.loadData()
}

// Ability 级刷新:从后台回到前台时刷新(可选)
// EntryAbility.onForeground → 通过 AppStorage 通知页面

十、本篇小结

知识点 核心要点
UIAbility HarmonyOS NEXT 应用的运行载体,管理生命周期和窗口
onCreate 冷启动时调用,适合初始化全局配置
onWindowStageCreate 窗口就绪,加载首页 + 初始化数据库的正确位置
onForeground/Background 前后台切换,处理资源恢复/暂停
时序注意 DB 初始化是异步的,页面 DAO 调用必须有 try/catch

参考资料

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


相关资源:

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐