HarmonyOS应用实战-启示散页-51-崩溃后别让上次操作消失:用 LastAction 账本辅助恢复入口
HarmonyOS 应用实战 51崩溃后别让上次操作消失用最小账本恢复入口而不重复写入用户在“答案之书”里导入题库、编辑内容或进入抽取动画时系统可能因为内存回收、闪退或强制关闭而中断。最危险的处理方式不是没有恢复而是把整份页面状态保存下来下一次启动后又把已经写入的数据再写一遍。恢复能力要保存的不是页面而是可判断的操作意图它做什么、作用于谁、走到哪个阶段。只有这样启动后才能决定继续进入某个页面、提示用户重新操作还是直接丢弃已失效的临时状态。先看现有启动链页面不是最早的恢复入口当前工程的EntryAbility.onWindowStageCreate明确要求在loadContent(pages/Index)前依次完成PreferencesStore.init(ctx)与SeedLoader.run(ctx)。代码注释给出的原因很具体如果 UI 已挂载而偏好仓储和默认题库还没准备好DeckPicker.aboutToAppear会读到空数据AppStorage的 watch 也不会替它补上那次初始化。这意味着恢复账本若要落地读取时机应在仓储初始化之后、页面恢复决策之前不能让Index或某个 Sheet 自己猜“上次用户在做什么”。阶段当前工程 owner恢复账本应做什么启动初始化EntryAbilityPreferencesStore确保账本可读取默认数据准备SeedLoader先恢复可用题库与currentDeckId页面导航NavigationNavPathStack只接收已校验的恢复意图题库写入DeckService以真实持久化结果决定是否已提交不要保存组件快照只保存恢复所需的最小事实State、动画进度、输入框光标、Sheet 展开状态都属于当前 ArkUI 组件它们在重启后没有可靠语义。真正值得跨启动保存的是操作类型、对象 id、阶段和时间。问题、答案全文和整份题库不应写进账本。typeRecoverableActionKinddraw|editDeck|importDeck;typeRecoverableActionPhasestarted|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):Promisevoid{constrecord:LastActionRecord{kind,phase:started,subjectId,createdAt:Date.now()};awaitPreferencesStore.setJson(PrefStoreName.App,last_action,record);}asyncmarkCommitted(record:LastActionRecord):Promisevoid{awaitPreferencesStore.setJson(PrefStoreName.App,last_action,{...record,phase:committed});}asyncclear():Promisevoid{awaitPreferencesStore.delete(PrefStoreName.App,last_action);}}started的意义是“可以向用户解释中断发生在哪类操作中”而非允许自动重放。committed的意义是“数据已经由仓储确认写入”恢复时绝不再调用一次保存。这样可以避免导入或新建题库在重启后被复制两次。保存动作如何接入不改变 DeckService 的责任不要把账本逻辑塞进DeckService.save的每一个内部细节。DeckService仍然负责数据校验、持久化和刷新协调器只负责在调用前后记录恢复语义。classDeckEditRecoveryCoordinator{constructor(privatereadonlyledger:LastActionLedger){}asyncsave(payload:SaveDeckPayload):PromiseDeckSummary{awaitthis.ledger.markStarted(editDeck,payload.id);constsummary:DeckSummaryawaitDeckService.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):PromiseRecoveryDecision{if(record.phasecommitted){return{kind:discard,reason:数据已经提交不应重复执行};}if(record.kindeditDeckrecord.subjectId){constdeck:Deck|nullawaitDeckService.get(record.subjectId);returndeck?{kind:resume-edit,deckId:deck.id}:{kind:discard,reason:题库已不存在};}if(record.kindimportDeck){return{kind:show-retry-import};}return{kind:discard,reason:抽取页不恢复未稳定动画};}这里故意没有“自动重新导入”或“自动重新抽取”。导入原文本可能没有保存抽取动画的中间状态没有业务价值恢复入口应让用户看见明确选择而不是偷偷再次执行动作。Navigation 只接收校验后的恢复参数项目当前路由参数已经区分DrawingParams、AnswerParams与DeckEditParams。账本恢复应复用这些已有契约而不是在NavPathStack里塞一份未知对象。asyncfunctionapplyRecovery(stack:NavPathStack,decision:RecoveryDecision):Promisevoid{if(decision.kindresume-edit){stack.pushPath({name:RouteName.DeckEdit,param:{deckId:decision.deckId}});return;}if(decision.kindshow-retry-import){// 当前工程没有导入恢复页这是建议新增入口不能伪称现有路由。return;}}resume-edit只在DeckService.get确认题库存在后发生。抽取与答案页则依赖更完整的参数和稳定结果不适合作为“崩溃后继续动画”的目标。四种中断分别怎样处理中断点账本状态恢复策略禁止做的事保存前退出started确认题库仍存在后可回编辑页自动调用saveRepository 保存后退出committed清理账本读取现有题库再次创建同名题库导入预览中退出started/importDeck提示重新选择导入内容假装原始文本仍在内存抽取动画中退出started/draw丢弃中间动画回到首页恢复未知随机中间态表格的判断依据是“是否已经形成稳定业务事实”不是用户感觉操作走到了第几步。只要没有可靠的提交证据就不能做自动重放。验证必须覆盖重复写入与冷启动建议把用例分成服务层和真机路径两部分模拟DeckService.save前抛错断言账本保留started题库数量不变。模拟 Repository 成功后、markCommitted前中断下一次启动先读取题库即便账本仍是 started也不能盲目重放保存。删除账本指向的自定义题库确认恢复决策为 discard不构造失效DeckEditParams。对 import 与 draw 两种 started 记录确认应用只提供重试入口或丢弃不保存输入正文与动画状态。真机强制结束应用后重启确认EntryAbility仍先完成PreferencesStore.init和SeedLoader.run再进入恢复决策。验收记录应至少包含 - 账本读写是否发生在仓储初始化之后 - committed 是否只在 Repository 成功后出现 - 恢复路径是否再次调用了写入服务 - 日志中是否只出现操作类型、对象 id 摘要与阶段 - 冷启动后当前题库与题库列表是否仍一致。常见误判与修复现象根因修复重启后出现两本相同题库started被当成可自动重放只恢复入口不重放写入恢复页跳转后白屏账本 id 没有经过DeckService.get校验校验存在性后再构造路由参数页面显示“已保存”数据却不存在点击时提前标 committedRepository 成功后才标记提交诊断记录泄露问题文本将完整 payload 直接写入账本账本只保留 kind、phase、id、时间小结恢复账本不是“页面存档”。它是启动期在仓储已就绪后读取的一小段业务证据什么操作中断了、是否已经提交、对象还能不能找到。把started与committed分开把恢复决策交给现有服务和路由契约就能避免应用在崩溃后既丢入口又重复写入。

相关新闻

物联网设备安全芯片SE050与STM32F767ZG的实战应用

物联网设备安全芯片SE050与STM32F767ZG的实战应用

1. 为什么物联网设备需要专用安全芯片?在2023年某智能家居厂商的数据泄露事件中,攻击者通过破解设备固件签名密钥,远程控制了超过10万台智能门锁。这个典型案例揭示了传统MCU方案在安全防护上的致命缺陷——即使采用STM32F7系列这样带有加密加…

2026/10/16 12:56:21 阅读更多 →
多线程(四)

多线程(四)

目录前言CAS什么是CASABA问题ABA问题的解决死锁死锁是什么如何避免死锁线程池ExecutorService 和 ExecutorsThreadPoolExecutor信号量 SemaphoreConcurrentHashMap前言 本文简单介绍了CAS, ABA问题, 死锁, 线程池以及ConcurrentHashMap CAS 什么是CAS 全称Compare and swap…

2026/10/6 4:54:07 阅读更多 →
专业级浏览器媒体资源嗅探工具:猫抓Cat-Catch技术解析与应用指南

专业级浏览器媒体资源嗅探工具:猫抓Cat-Catch技术解析与应用指南

专业级浏览器媒体资源嗅探工具:猫抓Cat-Catch技术解析与应用指南 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 猫抓Cat-Catch是一款基…

2026/10/4 2:21:33 阅读更多 →

最新新闻

跨领域迁移学习:打破认知牢笼,把经验炼成可迁移的思维模式

跨领域迁移学习:打破认知牢笼,把经验炼成可迁移的思维模式

本篇文章想聊一个很实际的话题:跨领域迁移学习。它不是讲机器的迁移学习,也不是讲考试刷题那种知识搬家,而是讲人类大脑如何把某个领域的内功心法,移植到另一个原本不相干的领域去。这个概念近几年在个人成长、产品设计、组织管理…

2026/10/16 12:57:54 阅读更多 →
论文数据分析全流程拆解:从清洗到回归的实战指南

论文数据分析全流程拆解:从清洗到回归的实战指南

论文写到数据分析这一步,很多人不是没数据,而是手里攥着一堆问卷结果、实验记录,却不知道该从哪下手。我接触过不少在写毕业论文和期刊论文的朋友,最常听到的一句话就是:“统计软件装了,按钮也点了&#xf…

2026/10/16 12:57:54 阅读更多 →
Anaconda环境迁移到Miniconda:目录映射无需复制文件

Anaconda环境迁移到Miniconda:目录映射无需复制文件

最近帮一个做数据分析的开发者处理环境迁移问题,他原来的机器上装的是 Anaconda,折腾了两年多,在里面建了七八个环境,每个环境里都装了一堆包。后来因为许可证和体积的原因想换到 Miniconda,但他死活不想重新创建那些环…

2026/10/16 12:57:54 阅读更多 →
FeurStagram源码构建完整教程:JDK 21 + Android SDK + Morphe补丁器,从零编译你自己的Instagram补丁

FeurStagram源码构建完整教程:JDK 21 + Android SDK + Morphe补丁器,从零编译你自己的Instagram补丁

【免费下载链接】FeurStagram Instagram without Reels, feed or ads: an open-source, updatable Instagram app for Android that keeps DMs and stories. 项目地址: https://gitcode.com/gh_mirrors/fe/FeurStagram 点击查看 免费下载 FeurStagram 是一个开源的…

2026/10/16 12:57:54 阅读更多 →
从一串怪字符说起:频率统计、信息熵与异常数据识别

从一串怪字符说起:频率统计、信息熵与异常数据识别

前几天排查一个内部系统的异常请求日志时,我看到了一条这样的输入: 11111111155555 。它安安静静躺在请求参数里,既不像正常的业务数据,也不像加密文本。第一反应当然是“脏数据,直接忽略”,但我还是花了…

2026/10/16 12:57:54 阅读更多 →
数据库代理实战:连接管理、读写分离与高可用架构的关键技术

数据库代理实战:连接管理、读写分离与高可用架构的关键技术

1. 为什么业务规模一上来,第一个想到的就是加一层代理先说一个我亲身踩过的场景。某业务早期只有一个数据库实例,应用直连,简单粗暴。等用户量上来,单实例扛不住,于是扩成了主从集群——一主两从,主库写&am…

2026/10/16 12:56:52 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →