Harmony学习之应用生命周期管理
一、场景引入
小明在上一篇文章中完成了登录跳转功能,现在他需要确保应用在不同状态下(前台、后台、销毁)能够正确管理资源。比如用户从登录页切换到其他应用时,需要暂停网络请求;应用从后台返回时,需要刷新用户数据;应用销毁时,需要清理定时器和缓存。本篇文章将系统讲解HarmonyOS的应用生命周期管理,帮助小明实现资源的合理分配和释放。
二、生命周期体系总览
HarmonyOS 5 API 12+的生命周期体系采用多层次结构,涵盖从应用启动到销毁的完整流程:
| 层级 | 核心作用 | 典型场景 |
|---|---|---|
| Application | 应用全局上下文管理 | 初始化全局服务、注册崩溃监控 |
| UIAbility | 功能单元的生命周期管理 | 首页Ability加载用户资产数据 |
| 页面(@Entry) | 页面级UI展示与交互 | 转账页面显示时刷新余额 |
| 组件(@Component) | 独立UI单元的渲染与资源释放 | 行情组件销毁时取消数据订阅 |
三、Application生命周期
Application是应用的全局上下文,管理所有Ability共享的资源,生命周期贯穿应用从启动到销毁的全流程。
1. 核心生命周期方法
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import { EncryptUtil } from '../common/EncryptUtil'; // 加密工具类export default class EntryAbility extends UIAbility {// 1. Ability创建时触发onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {console.log('Ability创建:初始化全局状态');// 金融场景:解密并加载本地存储的用户TokenEncryptUtil.decryptUserToken();// 初始化全局服务this.initGlobalServices();}// 2. 窗口舞台创建时触发onWindowStageCreate(windowStage: window.WindowStage) {console.log('窗口舞台创建:加载首页UI');// 必须在此回调中加载UI页面windowStage.loadContent('pages/Index', (err, data) => {if (err) {console.error('加载页面失败:', err);return;}console.log('页面加载成功');});// 配置窗口属性windowStage.getMainWindow((err, mainWindow) => {if (err) {console.error('获取主窗口失败:', err);return;}// 设置窗口背景色mainWindow.setWindowBackgroundColor('#FFFFFF');});}// 3. Ability进入前台时触发onForeground() {console.log('Ability回到前台:恢复服务');// 恢复在后台时暂停的操作this.resumeServices();}// 4. Ability进入后台时触发onBackground() {console.log('Ability进入后台:暂停服务');// 释放非必要资源,降低内存占用this.releaseResources();}// 5. 窗口舞台销毁时触发onWindowStageDestroy() {console.log('窗口舞台销毁:清理UI资源');// 释放窗口相关资源this.cleanupWindowResources();}// 6. Ability销毁时触发onDestroy() {console.log('Ability销毁:释放所有资源');// 清理全局资源this.cleanupGlobalResources();}// 7. 系统配置变化时触发(如屏幕旋转)onConfigurationUpdated(config: Configuration) {console.log('系统配置变化:适配新配置');// 适配横竖屏布局this.adaptLayout(config);}// 8. 系统内存不足时触发onTrimMemory(level: number) {console.log('系统内存不足:释放非核心资源');// 根据紧急程度释放资源this.releaseMemory(level);}
}
2. 生命周期方法详解
| 生命周期方法 | 触发时机 | 典型用途 |
|---|---|---|
onCreate() |
Ability首次创建时 | 初始化Ability级资源、获取上下文 |
onWindowStageCreate() |
窗口舞台创建时 | 加载UI页面、设置窗口属性 |
onForeground() |
Ability从后台回到前台时 | 恢复网络连接、刷新数据 |
onBackground() |
Ability进入后台时 | 暂停网络请求、释放资源 |
onWindowStageDestroy() |
窗口舞台销毁时 | 清理UI相关资源 |
onDestroy() |
Ability销毁时 | 释放所有资源、保存数据 |
onConfigurationUpdated() |
系统配置变化时 | 适配横竖屏、语言切换 |
onTrimMemory() |
系统内存不足时 | 释放非核心资源 |
四、页面生命周期
页面是被@Entry装饰的特殊组件,继承组件生命周期的同时,新增页面级交互相关函数。
1. 页面生命周期方法
@Entry
@Component
struct HomePage {@State userBalance: number = 0;private timerId: number = -1;// 1. 组件即将显示时触发(在build()之前)aboutToAppear() {console.log('页面即将显示:初始化数据');// 加载页面数据this.loadUserData();}// 2. 页面每次显示时触发onPageShow() {console.log('页面显示:刷新实时数据');// 启动定时器刷新数据this.startDataRefresh();}// 3. 页面每次隐藏时触发onPageHide() {console.log('页面隐藏:暂停操作');// 停止定时器this.stopDataRefresh();}// 4. 组件即将销毁时触发aboutToDisappear() {console.log('页面即将销毁:清理资源');// 清理定时器、解绑事件this.cleanup();}// 5. build()执行完毕后触发(API 12新增)onDidBuild() {console.log('页面构建完成:初始化UI');// 不建议在此修改状态变量this.initUI();}// 6. 用户点击返回按钮时触发onBackPress(): boolean {console.log('返回按钮被点击');// 返回true表示拦截返回,false表示允许返回return this.confirmExit();}build() {Column({ space: 20 }) {Text('我的资产').fontSize(20).fontWeight(FontWeight.Bold)Text(`总资产:${this.userBalance.toFixed(2)}元`).fontSize(18)}.width('100%').padding(16)}// 模拟数据加载private loadUserData() {// 实际场景:调用API获取用户数据this.userBalance = 50000;}// 启动数据刷新定时器private startDataRefresh() {this.timerId = setInterval(() => {// 模拟数据变化this.userBalance += Math.random() * 100 - 50;}, 5000);}// 停止数据刷新private stopDataRefresh() {if (this.timerId !== -1) {clearInterval(this.timerId);this.timerId = -1;}}// 清理资源private cleanup() {this.stopDataRefresh();}// 确认退出private confirmExit(): boolean {// 实际场景:检查是否有未保存的数据return false; // 允许返回}
}
2. 页面生命周期方法对比
| 方法 | 触发时机 | 执行次数 | 典型用途 |
|---|---|---|---|
aboutToAppear() |
组件首次创建时 | 1次 | 初始化数据、订阅事件 |
onPageShow() |
页面每次显示时 | 多次 | 刷新实时数据、启动动画 |
onPageHide() |
页面每次隐藏时 | 多次 | 暂停操作、保存状态 |
aboutToDisappear() |
组件即将销毁时 | 1次 | 清理定时器、解绑事件 |
onDidBuild() |
build()执行完毕后 | 1次 | 初始化UI、数据上报 |
onBackPress() |
点击返回按钮时 | 多次 | 拦截返回、确认操作 |
五、组件生命周期
普通组件(@Component装饰)的生命周期相对简单,主要关注组件的创建和销毁。
1. 组件生命周期方法
@Component
struct MarketCard {@State price: number = 0;private timerId: number = -1;build() {Column() {Text(`实时金价:${this.price.toFixed(2)}元/g`).fontSize(16).padding(10)}.backgroundColor('#f5f5f5').borderRadius(8)}// 组件即将显示时触发aboutToAppear() {console.log('行情卡片:开始监听实时价格');this.startPriceRefresh();}// 组件即将销毁时触发aboutToDisappear() {console.log('行情卡片:即将销毁,准备清理资源');this.stopPriceRefresh();}// build()执行完毕后触发onDidBuild() {console.log('行情卡片:构建完成');// 不建议在此修改状态变量}// 启动价格刷新定时器private startPriceRefresh() {this.timerId = setInterval(() => {this.price += Math.random() * 2 - 1; // 模拟价格波动}, 3000);}// 停止价格刷新private stopPriceRefresh() {if (this.timerId !== -1) {clearInterval(this.timerId);this.timerId = -1;}}
}
2. 组件生命周期方法对比
| 方法 | 触发时机 | 执行次数 | 典型用途 |
|---|---|---|---|
aboutToAppear() |
组件首次创建时 | 1次 | 初始化数据、启动定时器 |
aboutToDisappear() |
组件即将销毁时 | 1次 | 清理定时器、解绑事件 |
onDidBuild() |
build()执行完毕后 | 1次 | 初始化UI、数据上报 |
六、生命周期调用顺序
1. 应用启动流程
App进程启动
↓
Application.onCreate() → 初始化全局服务
↓
UIAbility.onCreate() → 初始化Ability级资源
↓
UIAbility.onWindowStageCreate() → 加载UI页面
↓
UIAbility.onForeground() → 应用进入前台
↓
页面.aboutToAppear() → 页面即将显示
↓
页面.build() → 构建UI结构
↓
页面.onDidBuild() → build执行完毕
↓
页面.onPageShow() → 页面显示
2. 应用进入后台
用户切换到其他应用
↓
页面.onPageHide() → 页面隐藏
↓
UIAbility.onBackground() → 应用进入后台
3. 应用返回前台
用户切回应用
↓
UIAbility.onForeground() → 应用回到前台
↓
页面.onPageShow() → 页面显示
4. 应用销毁流程
用户退出应用
↓
页面.onPageHide() → 页面隐藏
↓
页面.aboutToDisappear() → 页面即将销毁
↓
UIAbility.onWindowStageDestroy() → 窗口舞台销毁
↓
UIAbility.onDestroy() → Ability销毁
↓
Application.onDestroy() → 应用销毁
七、实战案例:登录状态管理
下面是一个完整的登录状态管理示例,综合运用了各个层级的生命周期管理:
1. Application层全局状态管理
// src/main/ets/Application.ts
import { Application } from '@ohos.app.ability.Application';
import { GlobalStateManager } from './common/GlobalStateManager';export default class MyApplication extends Application {onCreate() {console.log('应用启动:初始化全局状态');// 初始化全局状态管理器GlobalStateManager.getInstance().init();// 注册崩溃监控this.registerCrashMonitor();}onDestroy() {console.log('应用销毁:清理全局资源');// 清理全局资源GlobalStateManager.getInstance().cleanup();}private registerCrashMonitor() {// 实际场景:注册崩溃监控服务}
}
2. UIAbility层登录状态管理
// src/main/ets/entryability/EntryAbility.ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import { UserManager } from '../common/UserManager';export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {console.log('Ability创建:初始化用户状态');// 检查用户登录状态const isLoggedIn = UserManager.getInstance().isLoggedIn();if (isLoggedIn) {// 已登录,直接跳转到首页this.navigateToHome();} else {// 未登录,跳转到登录页this.navigateToLogin();}}onWindowStageCreate(windowStage: window.WindowStage) {console.log('窗口舞台创建:加载UI页面');// 根据登录状态加载不同页面const isLoggedIn = UserManager.getInstance().isLoggedIn();const targetPage = isLoggedIn ? 'pages/Home' : 'pages/Login';windowStage.loadContent(targetPage, (err, data) => {if (err) {console.error('加载页面失败:', err);return;}console.log('页面加载成功');});}onForeground() {console.log('Ability回到前台:验证登录状态');// 检查登录状态是否过期this.checkLoginStatus();}onBackground() {console.log('Ability进入后台:保存用户数据');// 保存用户数据到本地UserManager.getInstance().saveUserData();}onDestroy() {console.log('Ability销毁:清理资源');// 清理资源UserManager.getInstance().cleanup();}private navigateToHome() {// 实际场景:跳转到首页}private navigateToLogin() {// 实际场景:跳转到登录页}private checkLoginStatus() {// 实际场景:检查登录状态是否过期const isExpired = UserManager.getInstance().isTokenExpired();if (isExpired) {// 登录过期,跳转到登录页this.navigateToLogin();}}
}
3. 页面层数据管理
// src/main/ets/pages/Home.ets
import router from '@ohos.router';
import { UserManager } from '../common/UserManager';@Entry
@Component
struct Home {@State userInfo: any = {};@State balance: number = 0;private dataRefreshTimer: number = -1;aboutToAppear() {console.log('首页即将显示:加载用户数据');// 加载用户信息this.loadUserInfo();}onPageShow() {console.log('首页显示:启动数据刷新');// 启动定时器刷新数据this.startDataRefresh();}onPageHide() {console.log('首页隐藏:停止数据刷新');// 停止定时器this.stopDataRefresh();}aboutToDisappear() {console.log('首页即将销毁:清理资源');// 清理资源this.cleanup();}onBackPress(): boolean {console.log('返回按钮被点击');// 确认退出应用promptAction.showDialog({title: '确认退出',message: '确定要退出应用吗?',buttons: [{ text: '取消', color: '#666666' },{ text: '确定', color: '#FF3B30' }]}).then((result) => {if (result.index === 1) {// 确定退出this.exitApp();}});return true; // 拦截默认返回行为}build() {Column({ space: 20 }) {Text(`欢迎回来,${this.userInfo.name || '用户'}`).fontSize(24).fontWeight(FontWeight.Bold)Text(`账户余额: ${this.balance.toFixed(2)}元`).fontSize(18)Button('退出登录').width(200).onClick(() => {this.logout();})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}// 加载用户信息private loadUserInfo() {const userInfo = UserManager.getInstance().getUserInfo();this.userInfo = userInfo;this.balance = userInfo.balance || 0;}// 启动数据刷新定时器private startDataRefresh() {this.dataRefreshTimer = setInterval(() => {// 模拟数据刷新this.balance += Math.random() * 10 - 5;}, 10000);}// 停止数据刷新private stopDataRefresh() {if (this.dataRefreshTimer !== -1) {clearInterval(this.dataRefreshTimer);this.dataRefreshTimer = -1;}}// 清理资源private cleanup() {this.stopDataRefresh();}// 退出登录private logout() {promptAction.showDialog({title: '确认退出登录',message: '确定要退出登录吗?',buttons: [{ text: '取消', color: '#666666' },{ text: '确定', color: '#FF3B30' }]}).then((result) => {if (result.index === 1) {// 清除登录状态UserManager.getInstance().logout();// 跳转到登录页router.replaceUrl({url: 'pages/Login'});}});}// 退出应用private exitApp() {// 实际场景:退出应用AppControl.terminate();}
}
八、最佳实践与注意事项
1. 生命周期方法使用建议
| 生命周期方法 | 推荐操作 | 避免操作 |
|---|---|---|
onCreate() |
初始化全局配置、获取上下文 | 执行耗时操作、加载UI |
onWindowStageCreate() |
加载UI页面、设置窗口属性 | 执行耗时网络请求 |
onForeground() |
恢复网络连接、刷新数据 | 执行耗时操作 |
onBackground() |
暂停网络请求、释放资源 | 执行耗时操作 |
aboutToAppear() |
初始化页面数据、订阅事件 | 执行耗时操作 |
onPageShow() |
刷新实时数据、启动动画 | 执行耗时操作 |
onPageHide() |
暂停操作、保存状态 | 执行耗时操作 |
aboutToDisappear() |
清理定时器、解绑事件 | 修改状态变量 |
2. 性能优化建议
避免在生命周期方法中执行耗时操作:
// ❌ 不推荐:在生命周期方法中执行耗时操作
aboutToAppear() {// 耗时网络请求this.fetchDataFromServer(); // 可能导致页面卡顿
}// ✅ 推荐:使用异步任务
aboutToAppear() {// 启动异步任务TaskPool.execute(async () => {const data = await this.fetchDataFromServer();// 更新UIthis.updateUI(data);});
}
合理释放资源:
onBackground() {// 释放非必要资源this.releaseMemory();// 保存用户数据this.saveUserData();
}aboutToDisappear() {// 清理定时器this.cleanupTimers();// 解绑事件监听this.unbindEvents();
}
3. 常见问题与解决方案
问题1:内存泄漏
// ❌ 错误:未清理定时器
aboutToAppear() {this.timerId = setInterval(() => {// 定时任务}, 1000);
}// ✅ 正确:在aboutToDisappear中清理
aboutToDisappear() {if (this.timerId !== -1) {clearInterval(this.timerId);this.timerId = -1;}
}
问题2:状态不一致
// ❌ 错误:在onPageHide中修改状态
onPageHide() {this.userInfo = null; // 可能导致状态不一致
}// ✅ 正确:在aboutToDisappear中清理
aboutToDisappear() {this.userInfo = null;
}
问题3:网络请求未取消
// ❌ 错误:未取消网络请求
aboutToAppear() {this.requestId = http.request({// 请求配置});
}// ✅ 正确:在aboutToDisappear中取消请求
aboutToDisappear() {if (this.requestId) {http.cancelRequest(this.requestId);this.requestId = undefined;}
}
九、总结与行动建议
核心要点回顾
- 生命周期体系:掌握Application、UIAbility、页面、组件四个层级的生命周期管理
- 核心方法:理解
onCreate、onWindowStageCreate、onForeground、onBackground、onDestroy等核心生命周期方法 - 页面生命周期:掌握
aboutToAppear、onPageShow、onPageHide、aboutToDisappear、onBackPress等页面级方法 - 组件生命周期:掌握
aboutToAppear、aboutToDisappear、onDidBuild等组件级方法 - 调用顺序:理解应用启动、前后台切换、应用销毁时的生命周期调用顺序
行动建议
- 动手实践:按照本文示例,完成登录状态管理的完整实现
- 生命周期调试:在生命周期方法中添加日志,观察调用顺序和时机
- 性能优化:检查现有代码,确保在合适的生命周期方法中执行相应操作
- 资源管理:确保定时器、网络请求、事件监听等资源得到正确释放
- 状态管理:合理使用
@State、@Prop、@Link装饰器管理组件状态
通过本篇文章的学习,你已经掌握了HarmonyOS应用生命周期管理的核心能力。下一篇文章将深入讲解网络请求与数据获取,帮助你实现从服务器获取数据并展示在界面上。