第93篇 | HarmonyOS 生命周期刷新返回页面后数据为什么要重新读页面生命周期是训练营后期必须补上的工程底座。用户从系统相册选择照片、从分享面板返回、从视频任务页回来、从保险箱解锁再返回页面如果仍然使用旧状态就会出现“明明导入了却看不到”“任务生成了但状态没变”的体验问题。这一篇重点看aboutToAppear、onPageShow、aboutToDisappear和数据刷新函数之间的关系。生命周期刷新不是重复调用接口而是在正确的时机恢复监听、刷新记录、释放资源。本篇目标理解首次进入、再次显示、离开页面三个阶段各自做什么。确认返回页面后会重新读取相册记录和视频任务状态。看懂退出页面时为什么要释放分享监听、握姿监听和播放器。建立生命周期回归表覆盖导入、分享、解锁、视频任务返回。对应源码位置superImage/entry/src/main/ets/pages/Index.ets首次进入和再次显示都要补数据aboutToAppear负责首次进入页面时启动地图、加载相册、恢复视频状态、注册分享监听。onPageShow则处理应用重新回到前台后的刷新。两者职责接近但触发时机不同。如果只在首次进入读取数据用户从系统选择器导入照片后返回页面可能还是旧记录如果只在 onPageShow 读取首次打开又容易漏初始化。生命周期刷新把页面进入、返回和离开整理成清楚闭环aboutToAppear(): void { this.applyActiveSystemBarStyle(); this.prepareScenicAgentEntry(); void this.loadGalleryRecords(); void this.loadVideoManagerRecords(); void this.loadGalleryCloudSyncSession(); void this.registerNearbyShareListeners(); if (this.activeTab map) { void this.refreshCurrentLocation(true); void this.startHoldingHandAwareness(); } else if (this.activeTab camera) { this.scheduleCameraCapabilityPrepare(); } void this.loadVolcengineConfig(); this.backSurfaceController.setCreateHandler((surfaceId: string) { this.backSurfaceId surfaceId; this.scheduleCameraCapabilityPrepare(80); }); this.backSurfaceController.setDestroyHandler(() { this.backSurfaceId ; void this.teardownDualPreview(!this.shouldPreserveSequentialCaptureContext()); }); this.frontSurfaceController.setCreateHandler((surfaceId: string) { this.frontSurfaceId surfaceId; void this.ensureCameraPreview(); }); this.frontSurfaceController.setDestroyHandler(() { this.frontSurfaceId ; void this.teardownDualPreview(!this.shouldPreserveSequentialCaptureContext()); }); this.mapCallback async (err, controller) { if (err) { const message err.message err.message.length 0 ? err.message : JSON.stringify(err); this.mapReady false; this.mapErrorText 记忆地图初始化失败 ${err.code ?? -1}${message}; console.error([superImage][map] init failed code${err.code ?? -1} message${message}); return; } this.mapController controller; this.mapEventManager this.mapController.getEventManager(); this.mapReady true; this.mapErrorText ; this.showMapControllerIfActive(); this.bindMarkerClickEvent(); await this.primeMapCameraAtUserLocation(); await this.syncMapMarkers(); if (this.hasLiveLocation()) { this.focusMapAtCoordinate(this.currentLatitude, this.currentLongitude, false); } else { void this.refreshCurrentLocation(true); } }; } onPageShow(): void { this.applyActiveSystemBarStyle(); this.prepareScenicAgentEntry(); void this.loadGalleryRecords(); void this.loadVideoManagerRecords(); void this.loadGalleryCloudSyncSession(); void this.registerNearbyShareListeners(); if (this.activeTab map) { void this.refreshCurrentLocation(true); void this.startHoldingHandAwareness(); } else if (this.activeTab camera) { this.scheduleCameraCapabilityPrepare(); } void this.loadVolcengineConfig(); this.showMapControllerIfActive(); } onPageHide(): void { this.clearCameraCapabilityPrepareTimer(); this.unregisterNearbyShareListeners(); this.stopGalleryAntiPeepProtection(); this.hideMapController(); this.normalMovieVideoController.pause(); this.normalMoviePreviewPlaying false; void this.releaseNormalMoviePreviewMusic(); this.hideCameraCapturePreview(); this.stopLocationAwareness(); this.stopHoldingHandAwareness(); void this.teardownDualPreview(); } aboutToDisappear(): void {读取记录后要同步多个页面状态读取相册记录不是简单赋值。项目会在applyGalleryRecords后更新保险箱选中项、同步地图标记、修正详情页可见性。因为同一份记录同时支撑相册、地图、保险箱和回忆通知。这也是为什么生命周期刷新不能只刷新当前 tab。用户在相册导入照片地图页和保险箱页也可能依赖这份记录。相册记录刷新会影响多个页面不能只更新当前可见卡片const vaultRecords records.filter((record: GalleryMoment) record.visibility private); this.gallerySelectedId publicRecords.some((record: GalleryMoment) record.id this.gallerySelectedId) ? this.gallerySelectedId : (publicRecords.length 0 ? publicRecords[0].id : ); this.vaultSelectedId vaultRecords.some((record: GalleryMoment) record.id this.vaultSelectedId) ? this.vaultSelectedId : (vaultRecords.length 0 ? vaultRecords[0].id : ); this.syncGalleryFocus(records); this.syncGalleryGroupSelection(); } private async applyGalleryRecords(records: ArrayGalleryMoment): Promisevoid { const publicRecords records.filter((record: GalleryMoment) record.visibility ! private); this.galleryRecords records; this.capturePairCount records.length; this.syncRecordSelections(records); this.syncSelectedMapMemory(false); await this.syncMapMarkers(); this.updateAwarenessRecommendation(false);数据变更后要刷新媒体状态导入、移动保险箱、恢复公开相册、视频任务完成这些操作都会改变记录集合。项目用refreshGalleryMediaStateAfterMutation把保存、选中项、媒体 tab 和同步状态串起来避免某个页面遗留旧选择。这个函数的价值在于“变更后统一收口”。如果每个按钮各自改一部分状态后续很容易出现选中 id 不存在、详情页还打开、视频页仍显示旧记录。数据变更后统一刷新选中项和页面状态减少散落状态private async refreshGalleryMediaStateAfterMutation( preferredRecordId: string, scope: gallery | vault ): Promisevoid { const savedRecords await GalleryRecordService.loadRecords(this.getAbilityContext()); await this.applyGalleryRecords(savedRecords); const preferredRecord preferredRecordId.length 0 ? savedRecords.find((record: GalleryMoment) record.id preferredRecordId) : undefined; if (preferredRecord) { if (preferredRecord.visibility private || scope vault) { this.vaultSelectedId preferredRecord.id; } else { this.gallerySelectedId preferredRecord.id; this.selectedGalleryGroupKey this.buildGalleryRecordGroupKey(preferredRecord); this.galleryUserNoteDraft this.getRecordUserNote(preferredRecord); } } if (this.galleryViewMode detail !this.getFeaturedGalleryRecord()) { this.galleryViewMode album; this.stopGalleryAntiPeepProtection(); } if (this.vaultDetailVisible !this.getFeaturedVaultRecord()) { this.vaultDetailVisible false; } this.capturePairCount savedRecords.length; } private async appendGalleryRecord(record: GalleryMoment): Promisevoid { this.logCaptureTrace( append-gallery-record-start, recordId${record.id} pairIndex${record.pairIndex} backPath${record.backPath} frontPath${record.frontPath} ); const readyRecord record.aiStatus ready ? record : GalleryRecordService.applyLocalInsight(record); const nextRecords [readyRecord, ...this.galleryRecords.filter((item: GalleryMoment) item.id ! readyRecord.id)]; this.galleryRecords nextRecords; this.syncRecordSelections(nextRecords); this.gallerySelectedId readyRecord.id; this.selectedGalleryGroupKey this.buildGalleryRecordGroupKey(readyRecord); this.galleryUserNoteDraft this.getRecordUserNote(readyRecord); this.showCameraCapturePreview(readyRecord); this.syncSelectedMapMemory(true); this.capturePairCount nextRecords.length; this.galleryNoticeText this.hasGalleryFocus() ? this.getGalleryScopeDescription() : await this.syncMapMarkers(); this.updateAwarenessRecommendation(false); await this.persistGalleryRecords(nextRecords); this.gallerySelectedId readyRecord.id; this.selectedGalleryGroupKey this.buildGalleryRecordGroupKey(readyRecord); this.logCaptureTrace(离开页面时要释放能力aboutToDisappear不是可有可无。分享监听、握姿感知、视频播放器、相机资源都可能在页面不可见后继续占用资源离开页面时释放能减少后台异常和重复回调。发布前回归要包含“进入页面 - 打开系统相册/分享/认证 - 返回 - 切后台 - 再回来”的路径。只测首次启动很难覆盖生命周期问题。离开页面和返回路径都要处理当前 tab 的临时状态生命周期代码的验收标准不是“函数被调用”而是用户返回后看到的数据是新的离开后资源不再继续工作。工程验收表检查项通过标准首次进入相册、地图、视频任务、分享监听都能初始化。返回刷新从系统选择器、分享面板、认证页返回后数据不旧。变更收口导入、移动、恢复、删除后选中项仍有效。离开释放离开页面后监听和播放器不会继续占资源。真机复测口令从相册页拉起系统选择器导入照片返回应用后观察列表是否刷新从视频任务页提交任务后切到后台再回到前台观察状态是否恢复从地图页离开再回来确认监听和地图状态不会重复初始化。生命周期问题最适合按“进入 - 外部动作 - 返回 - 再离开”的路径复测。每条路径都要记录触发的是aboutToAppear、onPageShow还是aboutToDisappear避免把首次初始化和前台恢复混成同一件事。今日练习在onPageShow中打印刷新日志确认返回前台时确实触发。导入一张照片后立即返回相册检查记录数量是否变化。离开地图页再进入确认监听没有重复注册。