23-Ability回到前台数据还是旧的-分清窗口生命周期和页面刷新

📅 2026/7/14 0:08:29
23-Ability回到前台数据还是旧的-分清窗口生命周期和页面刷新
HarmonyOS 回到前台数据仍旧让 Ability 发信号让页面按 owner 刷新摘要UIAbility.onForeground表示应用回到前台不表示每个页面都已经拿到新数据。把业务对象直接塞进 Ability 生命周期会让首页、详情页和 Tab 刷新顺序失控。本文用轻量前台 tick 传递“该检查了”的信号页面在出现时从各自 Service 重新读取确保后台期间被其他入口修改的数据不会停在旧内存里。首页统计从后台回来仍是旧值切换一次 Tab 才更新通常不是组件没刷新而是把“窗口回前台”和“页面重新读取数据”混成了一件事。三种生命周期信号各做一件事信号owner应做什么不应做什么UIAbility.onForeground应用运行态发布前台通知直接改所有业务数据onPageShow页面读取本页读模型写其他页面状态Repository 写入数据层保存真实数据关心页面是否可见只有 Repository 是业务事实源生命周期只决定何时重新读取。启动时水合一个安全的轻量 tickexportconstRuntimeKeys{foregroundTick:foreground_tick}asconstexportfunctionhydrateRuntimeState():void{AppStorage.setOrCreatenumber(RuntimeKeys.foregroundTick,0)}在应用启动链路调用水合函数。这样页面使用StorageLink时不会在首次挂载遇到未初始化值tick 只携带时间戳不携带首页摘要或用户资料。Ability 只发布前台事件exportclassEntryAbilityextendsUIAbility{onForeground():void{AppStorage.setOrCreatenumber(RuntimeKeys.foregroundTick,Date.now())}}它不需要知道首页有几个卡片也不应该调用页面方法。应用层保持通用刷新决定权留在页面 owner 手中。页面按自己的读模型刷新StorageLink(RuntimeKeys.foregroundTick)foregroundTick:number0Statesummary:HomeSummarycreateEmptyHomeSummary()asynconPageShow():Promisevoid{this.summaryawaitthis.homeService.getSummary()}当页面重新可见时刷新是主路径如需监听 tick可在变化后调用同一个loadSummary()方法。不要直接在Builder内读取foregroundTick后串行发请求。刷新函数要可重入并丢弃过期响应后台恢复与用户下拉刷新可能同时发生。给加载操作序号防止慢请求覆盖新结果。privaterefreshId:number0privateasyncloadSummary():Promisevoid{constidthis.refreshIdconstvalueawaitthis.homeService.getSummary()if(idthis.refreshId)this.summaryvalue}这和搜索乱序是同一原则只有最新一次读取能写入页面状态。不同页面不要复用同一份业务快照页面刷新时机读取来源首页onPageShow、前台 tickHomeService.getSummary()详情页返回页面、显式刷新DetailService.getById()列表页写入成功后返回Repository.list()共享的是刷新信号不是把所有数据做成一份全局AppStorage对象。全局快照容易出现写入顺序和空值问题。覆盖这些真实路径后台期间通过另一入口修改数据再回到首页。前台切换时首页与详情页都不会读取对方的状态。快速前后台切换慢请求不覆盖新结果。首次启动、退出登录和清数据后tick 与页面默认值安全。rgonForeground|foreground_tick|onPageShow|AppStorageentry features common小结前台不是数据刷新本身而是一次刷新机会。让 Ability 发出小信号让页面从自己的服务重新读取才能既避免全局状态污染又保证用户回到应用时看见最新事实。不要把 onForeground 当成“每次都必须拉全量”前台信号也需要节制。短暂切到系统授权框再返回页面可能只需检查授权状态跨越较长时间或发现数据版本变化时才需要重新拉取完整列表。页面 owner 可以根据上次刷新时间、当前可见状态和业务版本做判断。privatelastLoadedAt:number0privateshouldRefresh(now:number):boolean{returnnow-this.lastLoadedAt30_000}这不是把数据缓存回页面而是避免无意义重复读取。真正影响结果的写入事件仍由服务和 Repository 处理页面只决定什么时候需要再次查询。写入成功后的返回刷新要走同一函数详情页保存成功返回首页时不要手工修改首页的某个计数。让首页在onPageShow调用和前台恢复相同的loadSummary()所有来源使用相同读模型。刷新来源调用方式目的页面首次出现onPageShow取得初始事实保存后返回onPageShow反映最新写入应用回前台前台信号后按需调用处理后台变化用户主动下拉loadSummary()明确要求刷新同一函数集中 loading、错误和序号判断避免四个入口各自更新一半状态。排查旧数据时按链路看日志出现旧值时依次确认写入是否真的成功、Repository 读取的是不是同一身份的数据、页面是否执行了刷新函数、慢请求是否被序号丢弃。不要先往 Ability 中塞更多全局变量那会暂时遮住问题却让数据 owner 更难辨认。排查点证据常见修复写入完成Repository 成功结果或事务日志修正保存失败被吞掉的分支身份一致当前 ownerId 与读取条件切换账号时清理页面状态页面触发onPageShow或刷新函数日志返回路由后补齐刷新调用响应新旧请求序号与完成顺序丢弃过期响应日志只需记录页面名、刷新来源、请求序号和错误码。业务内容由 Repository 管理不应完整打印到生命周期日志。前后台切换期间避免重复并发刷新在某些场景中onPageShow、前台 tick 和用户操作会在很短时间内连续到来。除了序号还可让服务层复用正在进行的同类读取避免对同一数据源发出多次并发请求。privatependingSummary?:PromiseHomeSummarygetSummary():PromiseHomeSummary{if(!this.pendingSummary){this.pendingSummarythis.repository.loadSummary().finally(()this.pendingSummaryundefined)}returnthis.pendingSummary}复用只适用于输入条件完全相同的读操作。账号、筛选条件或数据版本变化时必须新建请求否则“省请求”会重新变成旧数据问题。非可见页面不应抢占刷新资源Ability 回前台时没有必要让所有 Tab 同时刷新。页面可以检查自己是否可见或只记录 tick等真正进入onPageShow后再读取。这样既保留数据正确性也避免后台恢复瞬间出现多页并发加载、列表闪烁和耗电增加。当用户切换到某个 Tab页面使用最新 tick 与上次刷新时间判断是否需要更新这比由 Ability 穿透调用所有页面更符合 owner 边界。页面状态收到 tick 后的动作当前可见按时效策略调用刷新函数非当前 Tab只记录 tick展示时再刷新已销毁不执行任何页面操作正在加载复用同条件请求或让序号决定结果这一点在多 Tab 应用中尤其重要恢复前台不应让隐藏页面抢占网络与渲染资源也不应让它们直接写入当前页面的状态。若业务存在后台推送、同步或多窗口修改也沿用同一原则这些来源只发布“数据可能变化”的事件具体页面自己决定读取范围。这样未来增加新的数据来源时不会再把生命周期代码改成难以维护的业务调度中心。刷新失败时也不能把旧数据默认为成功数据。页面可以保留上次已成功的读模型以避免白屏但应显示更新时间或轻量错误提示并提供可访问的重试入口。对于需要强一致性的余额、权限或提交状态则不要继续展示可能过时的值应改为明确的加载或失败态。把“可暂时陈旧”和“必须立即确认”按业务拆开前后台恢复才既流畅又不误导用户。最后将一次前台恢复的刷新链路写进测试用例修改数据、离开应用、等待、回到首页、检查更新时间与错误态。它比只观察一次页面重绘更能证明生命周期、服务和 UI owner 已经真正接通。测试中同时记录是否发生重复请求避免以正确数据掩盖不必要的资源消耗。必要时用网络日志核对一次恢复只对应一次有效读操作。