鸿蒙应用开发实战【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.etsimport{UIAbility,Want,AbilityConstant}from'@kit.AbilityKit'import{hilog}from'@kit.PerformanceAnalysisKit'import{window}from'@kit.ArkUI'import{DatabaseService}from'./features/data/DatabaseService'constDOMAIN=0x0000// hilog 域值(自定义,16进制)constTAG='[EntryAbility]'// 日志 TAG,便于过滤exportdefaultclassEntryAbilityextendsUIAbility{// ...}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/catch4.2 为什么各页面必须有 try/catch
// LoginPage 进入首页时onPageShow():void{this.loadData()// 如果 DB 未就绪,会抛异常}privateasyncloadData():Promise<void>{try{this.cards=awaitCardDao.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){consttargetPage=want.parameters['targetPage']asstringif(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{// 获取主窗口constmainWindow=windowStage.getMainWindowSync()// 设置窗口全屏(内容延伸到状态栏下方)mainWindow.setWindowLayoutFullScreen(true).then(()=>{// 设置状态栏文字颜色为深色(适合浅色背景)constsysBarProps: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'constDOMAIN=0x0000constTAG='[EntryAbility]'exportdefaultclassEntryAbilityextendsUIAbility{/** 应用冷启动时调用一次 */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')// 异步初始化数据库,不阻塞 UIDatabaseService.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块正确处理,页面显示空状态而不是崩溃:
privateasyncloadData():Promise<void>{try{this.cards=awaitCardDao.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 配置说明
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
- HarmonyOS UIAbility 文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/uiability-overview
- Stage 模型概述:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/application-model-description