HarmonyOS7 生命周期:UIAbility 4 个关键回调你一定要搞懂
文章目录前言UIAbility 是什么4 个关键回调详解onCreate应用启动初始化就放这onWindowStageCreate窗口创建加载页面onForeground切到前台恢复状态onBackground切到后台保存状态WindowStage 生命周期与 UI 生命周期关系实战页面状态保存写在最后前言生命周期回调不是随便写的每个回调代表应用处于不同状态搞混了代码逻辑就全乱。我见过最多的低级 bug 之一在onCreate里初始化数据库结果切到后台再回来数据库又初始化了一遍数据全乱了。同学你压根没搞懂 UIAbility 的生命周期啊。UIAbility 是 HarmonyOS7 应用的基本单元——你可以理解为 Android 的 Activity但粒度更大。一个应用可以有多个 UIAbility每个对应最近任务列表里的一条记录。系统在调度 UIAbility 的时候会依次调用几个生命周期回调。你的代码写在哪个回调里直接决定了它的执行时机。写错位置轻则重复执行重则数据丢失。今天把这 4 个关键回调掰开了讲。UIAbility 是什么一句话UIAbility 是系统调度的基本单元提供窗口让 UI 渲染。所有页面和组件要显示必须依托一个窗口。UIAbility 就是创建窗口的那个壳——你写的Entry页面都是通过 UIAbility 的windowStage.loadContent()加载进去的。一个 UIAbility 可以管理多个页面通过路由跳转也可以只有单个页面。具体怎么拆分看业务需要。4 个关键回调详解UIAbility 的生命周期回调有 6 个但最核心的是这 4 个onCreate → onWindowStageCreate → onForeground → onBackground ↕ 可反复切换加上销毁阶段的onWindowStageDestroy和onDestroy一共 6 个。重点看前 4 个。onCreate应用启动初始化就放这系统创建 UIAbility 实例时触发整个生命周期只调一次。import{UIAbility,AbilityConstant,Want}fromkit.AbilityKit;import{window}fromkit.ArkUI;exportdefaultclassEntryAbilityextendsUIAbility{onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{console.info(onCreate: 应用启动)// 读取启动参数letparamwant.parameters?.[userId]asstringconsole.info(启动参数:${param})// 初始化全局资源数据库连接、网络配置等AppStorage.setOrCreate(globalConfig,{theme:dark})}}关键代码讲解want参数 —— 启动时传进来的数据别的 Ability 或系统拉起你时可以通过want.parameters传参launchParam—— 包含启动模式信息可以判断是冷启动还是热启动AppStorage.setOrCreate()—— 在这里设置全局数据所有页面都能访问血泪教训只放一次性初始化逻辑别把每次切回前台都要执行的代码放这里。onCreate只跑一次切后台再回来不会重新触发。onWindowStageCreate窗口创建加载页面onCreate之后系统创建窗口触发这个回调。加载入口页面就在这里。onWindowStageCreate(windowStage:window.WindowStage):void{console.info(onWindowStageCreate: 窗口已创建)// 设置窗口属性windowStage.getMainWindow().then((win:window.Window){win.setWindowLayoutFullScreen(true)letsysBarProps:window.SystemBarProperties{statusBarColor:#00000000,statusBarContentColor:#000000}win.setWindowSystemBarProperties(sysBarProps)})// 加载入口页面windowStage.loadContent(pages/Index,(err){if(err.code){console.error(页面加载失败:${err.code})return}console.info(页面加载成功)})}关键代码讲解windowStage.getMainWindow()—— 获取主窗口对象可以设置全屏、状态栏颜色等setWindowLayoutFullScreen(true)—— 设置沉浸式状态栏页面内容延伸到状态栏区域windowStage.loadContent(pages/Index)——这行最关键指定要加载的页面路径路径对应pages/目录下的文件页面加载是异步的回调里可以判断是否成功很多开发者喜欢在这里初始化数据库也可以但注意这只执行一次别重复初始化。onForeground切到前台恢复状态应用从后台切回前台时触发。用户看到的界面要刷新、暂停的任务要恢复都在这里处理。onForeground():void{console.info(onForeground: 切到前台)// 恢复暂停的任务this.resumeBackgroundTask()// 刷新可能过期的数据this.refreshDataIfNeeded()// 通知所有页面恢复AppStorage.setOrCreate(isForeground,true)}privateresumeBackgroundTask():void{lettaskInfoAppStorage.getstring(pausedTask)if(taskInfo){console.info(恢复任务:${taskInfo})}}privaterefreshDataIfNeeded():void{letlastUpdateAppStorage.getnumber(lastUpdateTime)||0letnowDate.now()if(now-lastUpdate30000){console.info(数据已过期重新拉取)AppStorage.setOrCreate(needRefresh,true)}}关键代码讲解这个回调每次从后台回到前台都会触发可以反复调用AppStorage.setOrCreate(isForeground, true)—— 页面组件可以通过StorageLink监听这个值自动响应前后台切换数据过期检查 —— 应用在后台可能很久回到前台时数据可能已经过时需要判断是否刷新这才是放切回前台逻辑的正确位置不是onCreateonBackground切到后台保存状态用户按 Home 键、切到别的 App应用进入后台触发onBackground。保存状态、暂停任务、释放资源就在这里。onBackground():void{console.info(onBackground: 切到后台)// 保存当前状态this.saveAppState()// 暂停耗时操作this.pauseOngoingTasks()// 释放不必要的资源this.releaseIdleResources()AppStorage.setOrCreate(isForeground,false)AppStorage.setOrCreate(lastUpdateTime,Date.now())}privatesaveAppState():void{letstate{currentPage:DetailPage,scrollPosition:256,formData:{name:测试,age:20}}AppStorage.setOrCreate(appState,JSON.stringify(state))}privatepauseOngoingTasks():void{console.info(暂停进行中的任务)}privatereleaseIdleResources():void{console.info(释放空闲资源)}关键代码讲解saveAppState()—— 把关键状态序列化存到 AppStorage下次回来可以恢复切后台后进程随时可能被杀重要数据必须在这里持久化不能指望下次onForeground还能恢复内存中的数据释放资源 —— 比如关闭不必要的文件句柄、取消网络请求等减少内存占用降低被系统杀掉的概率WindowStage 生命周期WindowStage 也有自己的生命周期夹在 UIAbility 生命周期之间onWindowStageCreate → onWindowStageDestroyonWindowStageCreate窗口创建时触发在这里加载页面onWindowStageDestroy窗口销毁时触发在 UIAbility 销毁之前执行如果你在onWindowStageCreate里做了资源分配比如创建数据库连接对应的清理逻辑应该放在onWindowStageDestroy里。与 UI 生命周期关系UIAbility 生命周期是宏观的组件生命周期是微观的。它们的关系UIAbility.onCreate │ UIAbility.onWindowStageCreate │ ▼ 页面 aboutToAppear ←── 组件 aboutToAppear │ ▼ 页面 onPageShow ←── 用户可见 │ ▼ UIAbility.onForeground │ ─ ─ ─ 用户使用中 ─ ─ ─ │ UIAbility.onBackground │ ▼ 页面 onPageHide ←── 用户不可见 │ ▼ UIAbility.onWindowStageDestroy │ ▼ UIAbility.onDestroy │ ▼ 页面 aboutToDisappear ←── 组件 aboutToDisappear关键理解UIAbility 生命周期管理的是进程级别的事组件生命周期管理的是 UI 级别的事。别搞混了。实战页面状态保存最常见的场景用户在详情页滑到一半切到别的 App回来后页面位置丢了。用生命周期回调来保存和恢复import{UIAbility,AbilityConstant,Want}fromkit.AbilityKit;import{window}fromkit.ArkUI;exportdefaultclassEntryAbilityextendsUIAbility{onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{AppStorage.setOrCreate(scrollOffset,0)AppStorage.setOrCreate(currentRoute,Index)}onWindowStageCreate(windowStage:window.WindowStage):void{windowStage.loadContent(pages/Index)}onForeground():void{letsavedOffsetAppStorage.getnumber(scrollOffset)||0if(savedOffset0){AppStorage.setOrCreate(restoreScroll,true)}}onBackground():void{AppStorage.setOrCreate(isForeground,false)AppStorage.setOrCreate(lastBackgroundTime,Date.now())}}页面侧配合恢复EntryComponentstruct DetailPage{StorageLink(scrollOffset)scrollOffset:number0StorageLink(restoreScroll)needRestore:booleanfalseprivatescroller:ScrollernewScroller()onPageShow():void{if(this.needRestorethis.scrollOffset0){setTimeout((){this.scroller.scrollOffset(this.scrollOffset)this.needRestorefalse},100)}}onPageHide():void{this.scrollOffsetthis.scroller.currentOffset().yOffset}build(){Scroll(this.scroller){Column(){ForEach([1,2,3,4,5,6,7,8,9,10],(item:number){Text(第${item}项内容).height(120)})}}}}关键代码讲解onPageHide里保存滚动偏移量到AppStorage—— 切后台时页面先隐藏这是最后的保存机会onForeground里设置restoreScroll标记 —— 通知页面需要恢复滚动位置onPageShow里执行恢复 —— 页面重新可见时根据标记和保存的偏移量滚动回去setTimeout延迟 100ms —— 等页面渲染完再滚动否则可能还没布局好就跳了写在最后UIAbility 生命周期就一句话onCreate 初始化一次onForeground 恢复状态onBackground 保存状态别搞混。最常犯的错误就是把本该放在onForeground的逻辑写到了onCreate结果冷启动正常、热启动出 bug。记住onCreate只调一次前后台切换不会重新触发——这点搞明白了生命周期的坑就踩得差不多了。