news 2026/7/29 19:56:42

HarmonyOS应用实战-启示散页-51-崩溃后别让上次操作消失:用 LastAction 账本辅助恢复入口

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS应用实战-启示散页-51-崩溃后别让上次操作消失:用 LastAction 账本辅助恢复入口

HarmonyOS 应用实战 51:崩溃后别让上次操作消失,用最小账本恢复入口而不重复写入

用户在“答案之书”里导入题库、编辑内容或进入抽取动画时,系统可能因为内存回收、闪退或强制关闭而中断。最危险的处理方式不是没有恢复,而是把整份页面状态保存下来,下一次启动后又把已经写入的数据再写一遍。

恢复能力要保存的不是页面,而是可判断的操作意图:它做什么、作用于谁、走到哪个阶段。只有这样,启动后才能决定继续进入某个页面、提示用户重新操作,还是直接丢弃已失效的临时状态。

先看现有启动链:页面不是最早的恢复入口

当前工程的EntryAbility.onWindowStageCreate明确要求在loadContent('pages/Index')前依次完成PreferencesStore.init(ctx)SeedLoader.run(ctx)。代码注释给出的原因很具体:如果 UI 已挂载而偏好仓储和默认题库还没准备好,DeckPicker.aboutToAppear会读到空数据,AppStorage的 watch 也不会替它补上那次初始化。

这意味着恢复账本若要落地,读取时机应在仓储初始化之后、页面恢复决策之前;不能让Index或某个 Sheet 自己猜“上次用户在做什么”。

阶段当前工程 owner恢复账本应做什么
启动初始化EntryAbility+PreferencesStore确保账本可读取
默认数据准备SeedLoader先恢复可用题库与currentDeckId
页面导航Navigation+NavPathStack只接收已校验的恢复意图
题库写入DeckService以真实持久化结果决定是否已提交


不要保存组件快照,只保存恢复所需的最小事实

@State、动画进度、输入框光标、Sheet 展开状态都属于当前 ArkUI 组件;它们在重启后没有可靠语义。真正值得跨启动保存的是操作类型、对象 id、阶段和时间。问题、答案全文和整份题库不应写进账本。

typeRecoverableActionKind='draw'|'editDeck'|'importDeck';typeRecoverableActionPhase='started'|'committed';interfaceLastActionRecord{kind:RecoverableActionKind;phase:RecoverableActionPhase;subjectId?:string;createdAt:number;}

这是一项建议新增的模型,并非当前工程已有实现。它的边界很窄:subjectId可以是题库 id,不能是整份SaveDeckPayloadphase只表达是否已经完成不可逆提交,不能冒充页面动画是否结束。

把 started 写在意图建立后,把 committed 写在仓储成功后

以编辑题库为例,DeckService.save的真实写路径是:清洗名称和答案、构造完整Deck、调用DeckRepository.saveDeck、最后更新LastDeckUpdateAt。因此账本的提交标记必须晚于 Repository 成功,不能在点击“保存”时立刻写成 committed。

classLastActionLedger{asyncmarkStarted(kind:RecoverableActionKind,subjectId?:string):Promise<void>{constrecord:LastActionRecord={kind,phase:'started',subjectId,createdAt:Date.now()};awaitPreferencesStore.setJson(PrefStoreName.App,'last_action',record);}asyncmarkCommitted(record:LastActionRecord):Promise<void>{awaitPreferencesStore.setJson(PrefStoreName.App,'last_action',{...record,phase:'committed'});}asyncclear():Promise<void>{awaitPreferencesStore.delete(PrefStoreName.App,'last_action');}}

started的意义是“可以向用户解释中断发生在哪类操作中”,而非允许自动重放。committed的意义是“数据已经由仓储确认写入”,恢复时绝不再调用一次保存。这样可以避免导入或新建题库在重启后被复制两次。

保存动作如何接入:不改变 DeckService 的责任

不要把账本逻辑塞进DeckService.save的每一个内部细节。DeckService仍然负责数据校验、持久化和刷新;协调器只负责在调用前后记录恢复语义。

classDeckEditRecoveryCoordinator{constructor(privatereadonlyledger:LastActionLedger){}asyncsave(payload:SaveDeckPayload):Promise<DeckSummary>{awaitthis.ledger.markStarted('editDeck',payload.id);constsummary:DeckSummary=awaitDeckService.save(payload);awaitthis.ledger.markCommitted({kind:'editDeck',phase:'started',subjectId:summary.id,createdAt:Date.now()});returnsummary;}}

这段协调代码的输入仍是现有SaveDeckPayload,输出仍是DeckSummary。它不复制DeckService的名称长度、最小答案数等校验规则,也不直接写AppStorage;后者应继续由DeckService在成功持久化后处理。

恢复时先判断对象是否还存在

账本记录并不保证对象还可用:用户可能在另一入口删除了题库,种子升级可能已恢复默认题库,或者 started 阶段根本还没有生成 id。恢复时应先让现有服务验证对象,再决定导航或清理。

typeRecoveryDecision=|{kind:'resume-edit';deckId:string}|{kind:'show-retry-import'}|{kind:'discard';reason:string};asyncfunctionresolveRecovery(record:LastActionRecord):Promise<RecoveryDecision>{if(record.phase==='committed'){return{kind:'discard',reason:'数据已经提交,不应重复执行'};}if(record.kind==='editDeck'&&record.subjectId){constdeck:Deck|null=awaitDeckService.get(record.subjectId);returndeck?{kind:'resume-edit',deckId:deck.id}:{kind:'discard',reason:'题库已不存在'};}if(record.kind==='importDeck'){return{kind:'show-retry-import'};}return{kind:'discard',reason:'抽取页不恢复未稳定动画'};}

这里故意没有“自动重新导入”或“自动重新抽取”。导入原文本可能没有保存,抽取动画的中间状态没有业务价值;恢复入口应让用户看见明确选择,而不是偷偷再次执行动作。

Navigation 只接收校验后的恢复参数

项目当前路由参数已经区分DrawingParamsAnswerParamsDeckEditParams。账本恢复应复用这些已有契约,而不是在NavPathStack里塞一份未知对象。

asyncfunctionapplyRecovery(stack:NavPathStack,decision:RecoveryDecision):Promise<void>{if(decision.kind==='resume-edit'){stack.pushPath({name:RouteName.DeckEdit,param:{deckId:decision.deckId}});return;}if(decision.kind==='show-retry-import'){// 当前工程没有导入恢复页;这是建议新增入口,不能伪称现有路由。return;}}

resume-edit只在DeckService.get确认题库存在后发生。抽取与答案页则依赖更完整的参数和稳定结果,不适合作为“崩溃后继续动画”的目标。

四种中断分别怎样处理

中断点账本状态恢复策略禁止做的事
保存前退出started确认题库仍存在后可回编辑页自动调用save
Repository 保存后退出committed清理账本,读取现有题库再次创建同名题库
导入预览中退出started/importDeck提示重新选择导入内容假装原始文本仍在内存
抽取动画中退出started/draw丢弃中间动画,回到首页恢复未知随机中间态

表格的判断依据是“是否已经形成稳定业务事实”,不是用户感觉操作走到了第几步。只要没有可靠的提交证据,就不能做自动重放。

验证必须覆盖重复写入与冷启动

建议把用例分成服务层和真机路径两部分:

  1. 模拟DeckService.save前抛错,断言账本保留started,题库数量不变。
  2. 模拟 Repository 成功后、markCommitted前中断,下一次启动先读取题库;即便账本仍是 started,也不能盲目重放保存。
  3. 删除账本指向的自定义题库,确认恢复决策为 discard,不构造失效DeckEditParams
  4. 对 import 与 draw 两种 started 记录,确认应用只提供重试入口或丢弃,不保存输入正文与动画状态。
  5. 真机强制结束应用后重启,确认EntryAbility仍先完成PreferencesStore.initSeedLoader.run,再进入恢复决策。
验收记录应至少包含: - 账本读写是否发生在仓储初始化之后; - committed 是否只在 Repository 成功后出现; - 恢复路径是否再次调用了写入服务; - 日志中是否只出现操作类型、对象 id 摘要与阶段; - 冷启动后当前题库与题库列表是否仍一致。

常见误判与修复

现象根因修复
重启后出现两本相同题库started被当成可自动重放只恢复入口,不重放写入
恢复页跳转后白屏账本 id 没有经过DeckService.get校验校验存在性后再构造路由参数
页面显示“已保存”,数据却不存在点击时提前标 committedRepository 成功后才标记提交
诊断记录泄露问题文本将完整 payload 直接写入账本账本只保留 kind、phase、id、时间

小结

恢复账本不是“页面存档”。它是启动期在仓储已就绪后读取的一小段业务证据:什么操作中断了、是否已经提交、对象还能不能找到。把startedcommitted分开,把恢复决策交给现有服务和路由契约,就能避免应用在崩溃后既丢入口又重复写入。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/29 19:54:51

5步实现智能自动化:彻底改变黑苹果配置的技术革命

5步实现智能自动化&#xff1a;彻底改变黑苹果配置的技术革命 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 在非苹果硬件上运行macOS一直是技术爱好…

作者头像 李华
网站建设 2026/7/29 19:51:01

单片机计算机毕设之基于 STM32 的阈值可调式温湿度智能控制系统研发 基于嵌入式的自动降温除湿与紫外线消毒硬件系统(012001)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/7/29 19:48:24

PixiJS Live2D插件:实现Web端高效动画渲染的5个核心解决方案

PixiJS Live2D插件&#xff1a;实现Web端高效动画渲染的5个核心解决方案 【免费下载链接】pixi-live2d-display A PixiJS plugin to display Live2D models of any kind. 项目地址: https://gitcode.com/gh_mirrors/pi/pixi-live2d-display 在Web应用开发中集成Live2D角…

作者头像 李华
网站建设 2026/7/29 19:46:39

Google Maps数据提取工具:5分钟掌握企业级地理信息采集方案

Google Maps数据提取工具&#xff1a;5分钟掌握企业级地理信息采集方案 【免费下载链接】google-maps-scraper scrape data from Google Maps. Extracts data such as the name, address, phone number, website URL, rating, reviews number, latitude and longitude, reviews…

作者头像 李华
网站建设 2026/7/29 19:46:27

Joplin搜索终极指南:3分钟掌握笔记查找的魔法

Joplin搜索终极指南&#xff1a;3分钟掌握笔记查找的魔法 【免费下载链接】joplin Joplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS. 项目地址: https://gitcode.com/GitHub_Trending/jo/joplin 你是…

作者头像 李华