HarmonyOS 6.0 页面生命周期与状态恢复 HarmonyOS的页面生命周期比Android简单——没有onStart/onStop/onResume那堆回调但简单不等于没有坑。UIAbility的生命周期、NavDestination的显示/隐藏、组件的aboutToAppear/aboutToDisappear这三层生命周期的执行顺序和交互逻辑如果不搞清楚状态管理就会一团糟。加上应用切后台被系统回收后恢复状态的需求生命周期的问题远比想象中复杂。UIAbility生命周期UIAbility是应用的入口组件生命周期回调import{UIAbility,AbilityConstant,Want}fromkit.AbilityKit;import{window}fromkit.ArkUI;classEntryAbilityextendsUIAbility{onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{// 应用创建初始化全局数据}onWindowStageCreate(windowStage:window.WindowStage):void{// 窗口创建加载主页面windowStage.loadContent(pages/Index);}onForeground():void{// 应用回到前台}onBackground():void{// 应用切到后台}onWindowStageDestroy():void{// 窗口销毁}onDestroy():void{// 应用销毁}}执行顺序onCreate → onWindowStageCreate → onForeground → (运行中) → onBackground → (后台) → onForeground → … → onBackground → onWindowStageDestroy → onDestroy关键onBackground不是onDestroy。应用切后台后可能长时间存活也可能随时被系统杀掉。不要在onBackground中做最终清理——那是onDestroy的事。NavDestination生命周期NavDestination有自己的显示/隐藏回调NavDestination(){// 页面内容}.onShown((){// 页面变为可见push到栈顶或返回到此页}).onHidden((){// 页面被覆盖新页面push或本页pop}).onWillAppear((){// 页面即将显示动画开始前}).onWillDisappear((){// 页面即将消失动画开始前}).onDisappeared((){// 页面已从导航栈移除})执行顺序push新页面时新页面的onWillAppear → onShown旧页面的onHidden。pop返回时当前页的onWillDisappear下级页面的onShown。onShown/onHidden可以反复触发——push覆盖触发onHiddenpop回来触发onShown。aboutToAppear只在组件首次创建时触发一次。组件生命周期Componentstruct MyComponent{aboutToAppear():void{// 组件即将构建在build之前// 适合做数据初始化}aboutToDisappear():void{// 组件即将销毁// 适合做资源清理}build(){// 构建UI}}aboutToAppear中不要做耗时操作。它在UI线程执行如果这里做网络请求或大量计算会阻塞首帧渲染。正确做法是aboutToAppear中发起异步请求UI先显示加载态数据回来后更新。三层生命周期协作UIAbility、NavDestination、Component三层生命周期的执行时序应用启动: UIAbility.onCreate → UIAbility.onWindowStageCreate → UIAbility.onForeground → Index页面 aboutToAppear → Index页面 build → Index页面 onShown push到详情页: Detail页面 aboutToAppear → Detail页面 build → Index页面 onHidden → Detail页面 onShown pop返回: Detail页面 onWillDisappear → Detail页面 aboutToDisappear → Index页面 onShown 应用切后台: UIAbility.onBackground → 所有页面保持不变组件不销毁 应用回前台: UIAbility.onForeground 应用被杀: 无回调下次启动走onCreate最大的坑应用切后台再回来组件不重新创建。aboutToAppear不会再次触发。如果页面数据可能过期比如切后台超过5分钟需要在onForeground或onShown中刷新。后台恢复策略应用在后台可能被系统回收。回来时如果进程还在数据还在内存中如果进程被杀需要从冷启动恢复。判断是否从冷启动恢复classEntryAbilityextendsUIAbility{privateisColdStart:booleantrue;onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{if(launchParam.launchReasonAbilityConstant.LaunchReason.STARTUP_CONTINUATION){// 从异常恢复启动this.isColdStartfalse;}else{this.isColdStarttrue;}}}STARTUP_CONTINUATION表示应用从异常终止后恢复。这个场景需要从持久化存储中恢复数据。关键状态持久化切后台前保存关键数据回来后恢复import{preferences}fromkit.ArkData;// 保存关键状态asyncsaveAppState():Promisevoid{letprefawaitpreferences.getPreferences(this.context,app_state);awaitpref.putSync(last_active_time,Date.now());awaitpref.putSync(current_page,this.currentPageName);awaitpref.putSync(scroll_position,this.scrollOffset);awaitpref.putSync(form_draft,this.draftContent);awaitpref.flush();}// 恢复关键状态asyncrestoreAppState():Promisevoid{letprefawaitpreferences.getPreferences(this.context,app_state);letlastTimepref.getSync(last_active_time,0)asnumber;letelapsedDate.now()-lastTime;if(elapsed5*60*1000){// 超过5分钟数据可能过期重新加载this.refreshAllData();}else{// 5分钟内恢复保存的状态this.scrollOffsetpref.getSync(scroll_position,0)asnumber;this.draftContentpref.getSync(form_draft,)asstring;}}在onBackground中调用saveAppState在onForeground中调用restoreAppState。超过5分钟的数据视为过期强制刷新。不要保存所有状态——只保存用户自己输入的、不可重新获取的数据。比如表单草稿、未发送的消息、编辑中的文本。列表数据、详情数据可以从网络重新加载不需要持久化。滚动位置恢复列表页跳详情再返回滚动位置丢失是最常见的体验问题privatescroller:ScrollernewScroller();privatesavedScrollOffset:number0;List(){// 内容}.scroller(this.scroller).onScroll((){this.savedScrollOffsetthis.scroller.currentOffset().yOffset;})// 返回时恢复onShown():void{if(this.savedScrollOffset0){setTimeout((){this.scroller.scrollTo({xOffset:0,yOffset:this.savedScrollOffset});},50);}}onScroll中持续记录偏移量。返回时scrollTo恢复。50ms延迟等列表渲染完成。问题如果返回时列表数据变化了比如删除了一条滚动位置可能对不上。精确恢复需要记录第一个可见项的id而非偏移量privatefirstVisibleId:string;.onScrollIndex((start:number){if(startthis.items.length){this.firstVisibleIdthis.items[start].id;}})// 恢复onShown():void{lettargetIndex-1;for(leti0;ithis.items.length;i){if(this.items[i].idthis.firstVisibleId){targetIndexi;break;}}if(targetIndex0){setTimeout((){this.scroller.scrollToIndex(targetIndex);},50);}}用id找index即使列表项有增删也能恢复到正确位置。表单数据保护用户填了一半表单跳到别的页面再回来输入内容丢失StateformDraft:stringprivateautoSaveTimer:number-1TextArea({text:$$this.formDraft}).width(100%).height(120).onChange((){this.scheduleAutoSave();})privatescheduleAutoSave():void{if(this.autoSaveTimer!-1){clearTimeout(this.autoSaveTimer);}this.autoSaveTimersetTimeout((){this.saveDraft();},1000);}privateasyncsaveDraft():Promisevoid{letprefawaitpreferences.getPreferences(this.context,drafts);awaitpref.putSync(draft_this.pageId,this.formDraft);awaitpref.flush();}输入变化后1秒自动保存草稿防抖。回到页面时从Preferences读取草稿aboutToAppear():void{this.loadDraft();}privateasyncloadDraft():Promisevoid{letprefawaitpreferences.getPreferences(this.context,drafts);this.formDraftpref.getSync(draft_this.pageId,)asstring;}草稿在提交成功后清除asyncsubmitForm():Promisevoid{awaitthis.api.submit(this.formDraft);letprefawaitpreferences.getPreferences(this.context,drafts);awaitpref.deleteSync(draft_this.pageId);awaitpref.flush();this.formDraft;}定时器与监听器清理组件销毁时必须清理所有异步资源privatetimers:number[][];privateemitterCallbacks:emitter.Callback[][];aboutToAppear():void{lett1setInterval((){this.tick();},1000);this.timers.push(t1);letcallback(data:emitter.EventData){this.onEvent(data);};this.emitterCallbacks.push(callback);emitter.on({eventId:1001},callback);}aboutToDisappear():void{for(leti0;ithis.timers.length;i){clearInterval(this.timers[i]);}for(leti0;ithis.emitterCallbacks.length;i){emitter.off(1001,this.emitterCallbacks[i]);}}规则在aboutToAppear中创建的定时器、监听器、订阅必须在aboutToDisappear中清理。每加一个setInterval/emitter.on/display.on就加对应的清理代码。不多不少。踩坑清单问题原因解决切后台回来数据没刷新aboutToAppear不再次触发在onForeground/onShown中刷新滚动位置丢失没记录偏移量scroller.currentOffset scrollTo表单内容丢失组件重建了草稿持久化到Preferences定时器泄漏aboutToDisappear没清理每个setInterval对应clearInterval页面重复初始化onShown和aboutToAppear都做了初始化初始化放aboutToAppear刷新放onShown进程被杀后状态丢失内存数据没持久化关键数据写PreferencesonHidden中做清理onHidden≠销毁还可能onShown回来清理放aboutToDisappear冷启动没恢复数据没判断launchReason检查STARTUP_CONTINUATIONscrollTo时机太早列表还没渲染完setTimeout 50ms延迟多次注册监听器每次onShown都onon只注册一次放aboutToAppear生命周期管理的核心原则创建和销毁成对显示和刷新分离关键数据持久化。aboutToAppear做初始化aboutToDisappear做清理onShown做刷新onBackground做保存。搞清楚每层回调的触发时机和生命周期状态管理就不会乱。