鸿蒙应用开发实战【06】— 应用入口与生命周期管理(EntryAbility 深度解析)
鸿蒙应用开发实战【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 完整生命周期图
图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, '首页加载成功')
})
}
关键设计决策:
-
数据库初始化使用
.then().catch()而不是await,原因:onWindowStageCreate本身不是async方法- 不需要等待 DB 就绪才显示 UI(页面有加载状态处理)
-
即使数据库初始化失败,应用仍然正常启动——各页面的 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 热启动时数据不刷新
现象:应用进入后台再回来,数据没有更新
解决:使用 onForeground 或 onPageShow 触发刷新:
// 页面级刷新:从子页面返回时刷新
onPageShow(): void {
this.loadData()
}
// Ability 级刷新:从后台回到前台时刷新(可选)
// EntryAbility.onForeground → 通过 AppStorage 通知页面
十、本篇小结
| 知识点 | 核心要点 |
|---|---|
| UIAbility | HarmonyOS NEXT 应用的运行载体,管理生命周期和窗口 |
| onCreate | 冷启动时调用,适合初始化全局配置 |
| onWindowStageCreate | 窗口就绪,加载首页 + 初始化数据库的正确位置 |
| onForeground/Background | 前后台切换,处理资源恢复/暂停 |
| 时序注意 | DB 初始化是异步的,页面 DAO 调用必须有 try/catch |
参考资料
- 鸿蒙应用开发实战【01】— 项目概述与开发环境搭建
- 鸿蒙应用开发实战【04】— 关系型数据库设计与封装
- UIAbility 开发指南
- UIAbility 生命周期
- Want 参数说明
- window API 参考
- hilog 日志参考
- module.json5 配置说明
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
更多推荐




所有评论(0)