ARTICLE DETAIL

资讯详情

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

【寻迹校园 HarmonyOS NEXT 实战 16】Preferences 还是 RelationalStore:HarmonyOS 本地存储选型实战

【寻迹校园 HarmonyOS NEXT 实战 16】Preferences 还是 RelationalStore:HarmonyOS 本地存储选型实战 【寻迹校园 HarmonyOS NEXT 实战 16】Preferences 还是 RelationalStoreHarmonyOS 本地存储选型实战这是“寻迹校园 HarmonyOS NEXT 实战”系列第 16 篇。本文不做抽象的 API 罗列而是结合项目里的安全提醒、失物记录、认领交接和照片文件说明 Preferences、RelationalStore 与应用沙箱文件系统各自应该保存什么以及错误选型会给升级、查询和数据一致性带来什么问题。上图为原创生成的技术插画不是项目截图。它表达的核心很简单偏好开关、结构化业务记录和二进制图片虽然都叫“本地数据”但生命周期、查询方式和失败影响完全不同不应该被塞进同一种存储。一、先看结论不要按“数据大小”单独做决定很多示例把存储选型简化成“少量数据用 Preferences大量数据用数据库”。这个判断不够。真正需要同时考虑的是数据有没有稳定主键和多字段结构是否需要筛选、排序、统计或关联是否存在状态机和增量迁移写入失败会不会破坏核心业务数据能否直接序列化成单个键值是否属于图片、音频等二进制内容后续是否需要跨版本演进。在“寻迹校园”中安全提醒只有一个布尔值适合 Preferences失物、认领、交接、举报都需要按 ID 查询并更新状态适合 RelationalStorePhoto Picker 返回的图片需要复制到应用沙箱由文件系统保存数据库只保留 URI 引用。二、项目里的 Preferences 只承担轻量偏好当前SettingsRepository.ets只保存一个安全提醒开关constSTORE_NAME:stringxunji_settings;constKEY_SAFE_NOTICE:stringsafe_notice_enabled;asyncloadSafeNotice(context:common.UIAbilityContext):Promiseboolean{conststoreawaitthis.ensureStore(context);constvalue:preferences.ValueTypeawaitstore.get(KEY_SAFE_NOTICE,true);returntypeofvalueboolean?value:true;}asyncsaveSafeNotice(context:common.UIAbilityContext,enabled:boolean):Promisevoid{conststoreawaitthis.ensureStore(context);awaitstore.put(KEY_SAFE_NOTICE,enabled);awaitstore.flush();}这里有三个值得保留的工程细节。第一读取给出默认值true。首次安装、键不存在或历史值异常时页面仍能得到可解释状态。第二读取后再次做类型检查。Preferences 的ValueType不只包含布尔值不能假设历史版本一定写入了正确类型。第三put()后调用flush()。内存中的变更与落盘完成不是同一个证明层级设置页提示“保存成功”前应等待持久化动作结束。三、为什么不把失物记录序列化进 Preferences把整个失物列表转成 JSON再保存到一个键看起来代码很短但会迅速遇到问题修改一条记录也要读出并重写整组数据无法自然表达按status、reportType、createdAt查询认领、交接和举报之间的关联只能靠手工遍历Schema 演进只能写一大段 JSON 兼容代码写入中断时整组数据可能一起受影响数据量增长后启动解析和全量复制成本持续上升。失物记录不是“多个设置项”而是结构化业务实体。项目为item_report维护主键、公开字段、私密核验字段、状态、事件日期、图片 URI 与创建时间这正是 RelationalStore 的职责范围。四、状态机数据必须有明确的权威来源项目中的报告状态包括状态含义是否参与首页公开筛选DRAFT尚未正式发布否OPEN开放展示和匹配是CLAIMING认领处理中是但业务动作受限RESOLVED已完成交接视页面语义展示不再作为新候选WITHDRAWN发布者主动撤回否HIDDEN治理流程隐藏否这些状态不是界面文案而是会影响编辑、删除、候选召回和后续交接的业务事实。若把它们分散成多个 Preferences 键跨实体约束会变得不可追踪。更合理的数据流是页面发起动作Service 校验状态Repository 原子地更新权威记录其他页面收到dataRevision失效信号后重新查询。刷新信号可以轻量权威数据不能变成临时页面变量。五、图片为什么既不放 Preferences也不直接塞数据库正文Photo Picker 返回的 URI 可能依赖临时授权。项目通过ReportPhotoRepository把图片复制到context.filesDir/report_photosRelationalStore 保存的是file://...URI 列表而不是把完整图片编码成超长字符串。这样可以避免 Preferences 或记录字段被大块二进制撑大也能在替换、删除记录时按引用清理文件。但文件系统和数据库也会产生新的顺序问题先复制图片再写入业务记录数据库失败时清理刚复制的文件编辑成功后删除不再引用的旧图片删除记录后清理其受管图片。这不是完整跨资源事务因此 Service 必须提供补偿逻辑不能让页面自己拼接文件操作。上图把数据落点和调用方向放在一张图里设置开关进入 Preferences业务实体进入 RelationalStore图片内容进入文件系统页面只调用 Service不直接持有任何一种存储实现。六、设置页为什么还需要 Service 层只有一个布尔值也可以直接在SettingsPage里调用 Preferences但项目仍保留SettingsServiceasyncloadSafeNotice():Promiseboolean{if(!this.context)returntrue;returnthis.repository.loadSafeNotice(this.context);}asyncsaveSafeNotice(enabled:boolean):Promiseboolean{if(!this.context)returnfalse;try{awaitthis.repository.saveSafeNotice(this.context,enabled);returntrue;}catch(error){returnfalse;}}这一层的价值不是“代码显得完整”而是隔离 Context、默认策略和错误映射。页面只处理loading、saving、当前开关值和用户可见错误不需要认识 Store 名称与 Key。未来如果安全提醒从单一开关升级为按场景控制Service 可以组合规则Repository 仍只负责读写。七、默认值不是随便写一个常量安全提醒默认开启是产品安全策略的一部分。默认值至少要在三个位置保持一致Repository 首次读取的默认值Service 没有 Context 时的保守回退页面初始化期间展示的本地状态。如果这三处分别使用true、false和空值用户会看到开关闪动或者在持久化尚未读取时错误关闭提醒。更成熟的做法是把默认值集中为领域常量并区分“尚未加载”和“已加载为 true”。当前页面通过loading控制在加载期间展示进度组件避免用户在未知状态下重复操作。八、flush 失败时不能假装已经保存设置页切换开关后先暂存原值再等待保存结果。如果持久化失败应恢复原值并显示明确提示。这种行为看似保守却能避免一个常见错觉界面上的 Toggle 已经改变用户以为下次启动仍会保持实际上磁盘写入失败。对于报告、认领等核心数据失败处理要求更高。页面不能只根据内存对象变化提示成功必须以 Repository 的权威写入完成为准。Preferences 和 RelationalStore API 不同但“成功提示要晚于持久化成功”的原则相同。九、选型决策表场景推荐方案关键理由安全提醒、主题偏好、少量开关Preferences键值读取简单有明确默认值失物记录、草稿、认领、交接、举报RelationalStore有主键、查询、状态更新和迁移需求用户选择的图片、未来的音频附件应用沙箱文件二进制内容适合文件 I/O数据库保存引用页面临时步骤、输入焦点、加载状态页面短生命周期状态不需要跨启动持久化跨设备同步、多人协作、真实审核远端服务与本地缓存需要身份、鉴权、冲突解决和审计选型不应从“我熟悉哪个 API”开始而应从数据契约开始。十、迁移策略也因存储类型不同Preferences 的迁移通常围绕 Key增加新 Key、读取旧 Key、转换类型、写入新值并删除废弃 Key。RelationalStore 的迁移围绕 Schema检查列、执行ALTER TABLE、回填旧数据、维护索引和版本幂等性。文件系统迁移则要处理目录、扩展名、孤儿文件和 URI 失效。三种存储混在一起时升级顺序必须明确。例如先升级数据库字段再扫描文件引用最后清理孤儿文件任何一步都要可重复执行。当前项目规模较小没有必要为一个开关引入复杂配置数据库但也不能因此把所有数据都降级成键值。十一、隐私与安全边界本地保存不等于天然安全。项目仍需要遵守这些边界Preferences 不写入账号密码、证书口令或长期 Token公开ItemReport与私密核验特征分开读取图片目录只保存用户主动选择并用于发布的内容日志不打印私密特征和完整本机路径删除业务记录时同步考虑受管图片未来接入云端后权限校验必须放在服务端不能依赖本地 UI 隐藏。当前项目是单机比赛演示版Preferences、RelationalStore 和沙箱文件只能解决本设备数据管理不能证明真实账号隔离、多设备同步或运营审计已经实现。十二、验证应该分三层第一层是静态检查Store 名称、Key、默认值、flush()、Repository 接口和页面状态是否一致。第二层是构建与自动化确认 ArkTS 类型、模块依赖和不依赖 Context 的业务测试没有回归。第三层是模拟器或真机验证切换安全提醒后重启应用确认值仍保持创建、编辑和删除记录后重启确认 RelationalStore 与文件目录一致。可执行的项目命令以当前脚本为准powershell-ExecutionPolicy Bypass-File.\scripts\test-local.ps1 powershell-ExecutionPolicy Bypass-File.\scripts\build-hap.ps1本地 Node 测试不具备UIAbilityContext因此不能证明 Preferences 真正落盘也不能证明 RelationalStore 的 Schema、加密和设备 I/O。只有在模拟器或真机完成重启复测才能把对应项标记为passed。十三、什么时候需要升级设计当设置项增加到需要分组、搜索、版本迁移或策略组合时可以为设置建立明确模型当业务数据需要远端同步时应引入本地/远端 Repository 与冲突策略当图片数量和体积增长时应增加配额、压缩、引用计数和孤儿清理任务。升级的前提是需求出现而不是为了让目录看起来更复杂。当前xunji_settings只有安全提醒开关Preferences 已经足够报告状态机具有结构化查询需求RelationalStore 才是合适选择。十四、把存储选型变成可审查的工程决策存储方案不能只写在代码里还应留下可复核的决策记录。每新增一种数据先说明数据所有者、生命周期、查询方式、失败影响、迁移方式与清理责任再决定进入 Preferences、RelationalStore 还是文件系统。这样评审者看到的不只是 API 调用而是一条从业务语义到技术落点的完整推导。对“寻迹校园”而言安全提醒由设置域负责报告和认领由业务 Repository 负责图片由文件 Repository 负责。三类数据可以在同一页面出现但不能因此共用同一种存储。页面只是把动作交给 ServiceService 再协调各数据源并把失败映射成用户能够理解的状态。十五、失败场景、补偿顺序与回滚边界真实故障通常发生在跨资源写入之间。例如图片已经复制成功但报告写库失败或者数据库删除成功文件清理却失败。工程上要先保护权威业务记录再处理可补偿资源并把孤儿文件扫描作为后续维护能力而不是让页面静默吞掉异常。新增失败删除本轮新复制且尚未被任何记录引用的图片编辑失败保留旧记录和旧图片清理本轮新增文件删除失败不提前删除图片避免仍存在的记录变成断图文件清理失败记录可观测事件允许后台或下次启动重试迁移失败保留旧版本字段或备份禁止只完成一半就提升 Schema 版本。这些补偿并不等于真正的跨资源事务。若未来数据进入云端还需要幂等请求、服务端事务、冲突版本和重试上限不能把本地演示中的顺序控制直接描述成分布式一致性。十六、验收证据应覆盖哪些层级验收时应把静态代码、自动化、构建、设备落盘和冷启动恢复分开记录。看到flush()只能证明调用意图Node 测试通过只能证明普通规则只有在真实 UIAbilityContext 下保存并重启仍保持才能证明 Preferences 或 RelationalStore 的设备持久化路径。一份可追踪记录至少包含执行命令、设备或模拟器型号、测试数据、预期结果、实际结果、失败日志与未验证项。这样后续更换 SDK、调整 Schema 或扩展跨校数据时可以判断回归来自业务规则、存储实现还是运行环境。十七、本文小结HarmonyOS 本地存储选型的关键不是简单比较 API而是识别数据语义偏好是键值业务记录是结构化实体图片是文件资源。“寻迹校园”用 Preferences 保存安全提醒用 RelationalStore 保存报告、草稿与状态机数据用应用沙箱保存图片并通过 Page → Service → Repository 保持边界。这样的拆分让默认值、失败恢复、迁移和验证都更容易解释也避免把页面上的一次切换误当成完整的持久化成功。系列导航第 16 篇 / 共 50 篇。上一篇《Repository 双数据源》下一篇《编辑、撤回、删除与结案的一致性设计》。
返回列表