ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Harmony os 技术实战|拼豆制图40:让“本机数据”真正跨重启保存

Harmony os 技术实战|拼豆制图40:让“本机数据”真正跨重启保存 个人页展示“本机数据”收藏和最近生成也能在当前运行期即时更新但源码里的favoriteIds、generatedRecords都是页面State收藏每次从三个默认 ID 开始最近记录每次从空数组开始。应用进程结束后这些变化不会自动恢复。工程虽然注册了EntryBackupAbility并允许备份恢复两个回调目前也只输出日志没有实际数据读写。本篇以这一真实差距为起点使用kit.ArkData中的 Preferences 保存小型 JSON 快照并把完整 4900 格图纸留给独立文件。目标不是把所有状态塞进一个键而是建立页面、服务、仓库、迁移和备份各自清晰的责任边界。一、先证明当前状态为什么不会跨重启页面字段直接初始化StatefavoriteIds:string[][anime-1,idol-1,designer-1];StategeneratedRecords:Pattern[][];收藏切换和最近记录只给这些数组重新赋值没有调用 Preferences、文件或数据库。State负责 ArkUI 响应式刷新不是持久化介质。备份扩展同样没有写数据asynconBackup(){hilog.info(DOMAIN,testTag,onBackup ok);awaitPromise.resolve();}asynconRestore(bundleVersion:BundleVersion){hilog.info(DOMAIN,testTag,onRestore ok %{public}s,JSON.stringify(bundleVersion));awaitPromise.resolve();}“允许备份”只说明系统可以调用扩展不等于应用状态已经进入备份内容。必须先有真实本地存储再设计备份和恢复。二、Preferences 适合存什么不适合存什么本应用的数据可分两类数据建议介质原因收藏 ID、最近摘要、设置、版本号Preferences体积小键值读取简单4900 格完整图、1225 格预览独立 JSON 文件体积大按作品读写更合理导出的 PNG相册/应用文件二进制资源不应放键值 JSONPreferences 的值可以保存字符串因此小型结构可用JSON.stringify()。不要每切一次收藏就重写十张完整图纸也不要让单个 Preferences 键随着作品数无限增长。建议快照interfaceUserIndexSnapshot{schemaVersion:number;favoriteIds:string[];recent:PatternSummary[];updatedAt:number;}三、先用适配器隔离系统 API当前 SDK 可从kit.ArkData导入preferences通过getPreferences()获取实例get()/put()读写值flush()把内存中的修改保存到文件。不要让页面到处直接调用这些接口。定义最小适配器import{preferences}fromkit.ArkData;import{common}fromkit.AbilityKit;exportclassPreferencesStore{privatestore?:preferences.Preferences;constructor(privatereadonlycontext:common.UIAbilityContext){}privateasyncinstance():Promisepreferences.Preferences{if(this.storeundefined){this.storeawaitpreferences.getPreferences(this.context,pindou_user_data);}returnthis.store;}}实例按名字对应持久化文件并在类内缓存。页面只依赖仓库接口系统 API 的导入、文件名和错误处理集中在一处。四、读写字符串时 put 之后还要 flush适配器增加asyncloadString(key:string):Promisestring{conststoreawaitthis.instance();constvalueawaitstore.get(key,);returntypeofvaluestring?value:;}asyncsaveString(key:string,value:string):Promisevoid{conststoreawaitthis.instance();awaitstore.put(key,value);awaitstore.flush();}put()修改 Preferences 对象中的值flush()异步保存到文件。若只调用put()就立刻宣称重启可恢复数据落盘时机并不完整。读取时要验证类型因为get()返回通用值类型。默认值集中在仓库页面不应在多个位置各写一套默认收藏。同步getSync、putSync和较新版本的flushSync虽然存在但页面启动和大字符串写入更适合异步避免阻塞 UI 线程。五、仓库负责 JSON、默认值和迁移定义仓库exportinterfaceUserDataRepository{loadIndex():PromiseUserIndexSnapshot;saveIndex(snapshot:UserIndexSnapshot):Promisevoid;}实现读取asyncloadIndex():PromiseUserIndexSnapshot{constrawawaitthis.store.loadString(user_index);if(raw.length0)returnthis.defaultSnapshot();try{constparsedJSON.parse(raw)asUserIndexSnapshot;returnthis.migrateAndNormalize(parsed);}catch(_){returnthis.defaultSnapshot();}}解析失败不能让首页崩溃。可以回退默认值并保留错误日志若数据价值高还可先复制损坏原文到恢复文件再创建新快照。schemaVersion从 1 开始。未来字段改名、最近摘要增加文件路径时按版本逐步迁移不要让页面理解旧结构。六、服务层统一去重、限长和保存顺序页面不应先改数组再自行序列化。服务接收业务操作并返回最新快照exportclassUserContentService{constructor(privatereadonlyrepository:UserDataRepository){}asynctoggleFavorite(id:string):PromiseUserIndexSnapshot{constcurrentawaitthis.repository.loadIndex();constexistscurrent.favoriteIds.indexOf(id)0;constnextIdsexists?current.favoriteIds.filter(itemitem!id):current.favoriteIds.concat([id]);constnext{...current,favoriteIds:nextIds,updatedAt:Date.now()};awaitthis.repository.saveIndex(next);returnnext;}}最近记录的置顶、去重和最多 10 条也放在这里。页面得到成功返回后一次性更新constsnapshotawaitthis.userContentService.toggleFavorite(pattern.id);this.favoriteIdssnapshot.favoriteIds;这样 UI 展示的是已经保存成功的状态。若写入失败可以保留旧数组并显示重试提示不会出现页面说“已收藏”但重启后消失。七、页面启动要有加载态和失败态aboutToAppear()中恢复数据StateuserDataReady:booleanfalse;StateuserDataError:string;asyncaboutToAppear():Promisevoid{try{constsnapshotawaitthis.userContentService.load();this.favoriteIdssnapshot.favoriteIds;this.generatedRecordsawaitthis.loadRecentPatterns(snapshot.recent);}catch(error){this.userDataError(errorasError).message;}finally{this.userDataReadytrue;}}在userDataReadyfalse时不要先渲染三个默认收藏再突然替换否则用户会看到闪变也可能在加载完成前点击产生并发写入。可以显示轻量骨架或暂时禁用写操作。默认数据应该属于首次启动策略而不是每次页面构造策略。仓库确认没有user_index键时才返回默认收藏已有空收藏必须保持空不能被默认值再次填回。八、完整图纸按 ID 存文件Preferences 只留索引Pattern包含 4900 个chartCells和 1225 个previewCells十张完整 JSON 会很大。建议目录filesDir/ └─ user_patterns/ ├─ user-generated-...json └─ user-generated-...json Preferences:user_index └─ recent: [{ id, title, width, height, colorCount, updatedAt }]保存新作品的事务顺序1. 把完整图写入临时文件 2. 关闭并完成文件替换 3. 更新 Preferences 摘要索引并 flush 4. 页面接收新快照如果第 1 步失败索引不应出现悬空 ID如果第 3 步失败完整文件成为孤儿可在下次启动扫描并清理或恢复。对本地数据也要明确部分失败的处理方式。九、备份能力要建立在真实文件之上backup_config.json当前允许备份恢复但EntryBackupAbility没有声明需要备份哪些数据也没有迁移动作。完成 Preferences 与作品文件后才有可备份对象Preferences 文件收藏、最近摘要、设置、schemaVersion user_patterns 目录用户生成的完整图纸 不必备份 cacheDir导出缓存是临时数据 相册 PNG由系统媒体库管理不应当作应用缓存重复打包恢复时先还原文件再由仓库按schemaVersion迁移。BundleVersion可辅助判断来源版本但数据自身版本仍然必要因为应用升级和备份恢复不总是一一对应。日志写“onBackup ok”只能证明回调返回不能证明收藏与图纸可恢复。验收必须卸载/恢复或使用官方备份流程实际核对数据。十、并发写入要串行页面不能各写各的收藏、导出和生成都可能更新同一索引。若三个处理器各自执行“load → 修改 → save”会发生丢更新A 与 B 读到同一旧快照B 的后写结果覆盖 A。服务层可维护写队列privatewriteChain:PromisevoidPromise.resolve();privateenqueue(task:()Promisevoid):Promisevoid{this.writeChainthis.writeChain.then(task,task);returnthis.writeChain;}所有索引写入经同一UserContentService排队任务内部读取最新快照后再修改。不要让收藏卡片直接写 Preferences、导出页直接写 JSON、个人页又单独维护计数。页面中的作品数和收藏数应从保存后的同一快照派生而不是每个操作手工加一减一。十一、重启回归才是持久化的基本验收建议执行完整序列首次启动确认默认收藏只初始化一次。取消全部收藏强制结束应用再打开仍保持空收藏。生成两张图重启后最近顺序和内容可恢复。同一图重复导出最近列表不重复。人为写入旧schemaVersion样本启动后迁移到新结构。人为损坏 JSON页面进入可恢复状态而不是白屏。连续快速收藏和生成重启后两类变化都存在。执行备份恢复流程核对 Preferences 与完整图文件而不是只看回调日志。若当前运行正常但重启丢失先确认flush()是否完成若空收藏重启后又出现三个默认项默认值判断把“无键”和“空数组”混淆了若最近摘要存在但点开失败检查完整文件是否与索引按同一 ID 写入。十二、结语“本机数据”首先意味着进程结束后仍能恢复而不是页面状态能即时刷新。拼豆制图当前的State和空备份回调提供了 UI 与扩展骨架真正落地还需要 Preferences 小索引、独立作品文件、单一服务写路径、schemaVersion 和重启回归。让 Preferences 只保存收藏 ID、最近摘要和设置让大图按 ID 独立存储再把备份建立在这些真实文件之上既能控制写入体积也为迁移、清理和恢复留下清晰边界。持久化完成后“本机数据”才从一句页面标签变成可验证的产品能力。
返回列表