当前位置: 首页 > news >正文

HarmonyOs应用《重要日》开发第6篇 - 数据持久化存储

本篇深入剖析 ImportantDays 项目的数据持久化方案——基于 HarmonyOS ArkData 模块的 Preferences 轻量级存储,以及 PreferenceUtil 工具类的单例封装。

一、HarmonyOS 数据存储方案对比

HarmonyOS 提供了多种数据存储方案:

方案适用场景特点
Preferences轻量级 KV 数据同步操作,适合少量数据
关系型数据库 (RDB)结构化数据SQLite,支持复杂查询
分布式数据 (DDS)跨设备同步多设备数据一致性
文件存储大文件直接读写文件系统

ImportantDays 选择 Preferences 的原因:

  1. 数据量小(通常几十条重要日)
  2. 数据结构简单(JSON 字符串)
  3. 不需要复杂查询
  4. 同步操作,代码简洁

二、PreferenceUtil 工具类

2.1 完整实现

// utils/PreferenceUtil.etsimport{preferences}from'@kit.ArkData';import{common}from'@kit.AbilityKit';import{Logger}from'./Logger';exportclassPreferenceUtil{privatestaticpreferenceRecord:Map<string,PreferenceUtil>=newMap();privatedataPreferences:preferences.Preferences|null=null;privateconstructor(context:Context,fileName:string){preferences.removePreferencesFromCacheSync(context,fileName);this.dataPreferences=preferences.getPreferencesSync(context,{name:fileName});Logger.info(`PreferenceUtil:${fileName}init:${this.dataPreferences!=null}`);}publicstaticgetInstance(context?:Context,fileName:string='default'):PreferenceUtil{if(PreferenceUtil.preferenceRecord.has(fileName)){returnPreferenceUtil.preferenceRecord.get(fileName)!;}constctx:Context=context||getContext()ascommon.UIAbilityContext;constpreferenceUtil:PreferenceUtil=newPreferenceUtil(ctx,fileName);PreferenceUtil.preferenceRecord.set(fileName,preferenceUtil);returnpreferenceUtil;}publicgetPreferences():preferences.Preferences|null{returnthis.dataPreferences;}publicput(key:string,value:object|string|number|boolean):void{if(!this.dataPreferences){Logger.info('PreferenceUtil: dataPreferences is null');return;}try{this.dataPreferences.putSync(key,value);this.dataPreferences.flushSync();Logger.info(`PreferenceUtil: put:${key}`);}catch(e){Logger.error(`PreferenceUtil: put error:${JSON.stringify(e)}`);}}publicget(key:string,defaultValue:object|string|number|boolean):object|string|number|boolean{if(!this.dataPreferences){returndefaultValue;}try{returnthis.dataPreferences.getSync(key,defaultValue)asobject|string|number|boolean;}catch(e){Logger.error(`PreferenceUtil: get error:${JSON.stringify(e)}`);returndefaultValue;}}publicdelete(key:string):void{if(!this.dataPreferences){return;}try{this.dataPreferences.deleteSync(key);this.dataPreferences.flushSync();Logger.info(`PreferenceUtil: delete:${key}`);}catch(e){Logger.error(`PreferenceUtil: delete error:${JSON.stringify(e)}`);}}}

2.2 设计要点

单例模式(按文件名区分)
privatestaticpreferenceRecord:Map<string,PreferenceUtil>=newMap();publicstaticgetInstance(context?:Context,fileName:string='default'):PreferenceUtil{if(PreferenceUtil.preferenceRecord.has(fileName)){returnPreferenceUtil.preferenceRecord.get(fileName)!;}// ...创建新实例}

使用MapfileName维护多个实例,每个文件对应一个PreferenceUtil实例。

缓存清理
privateconstructor(context:Context,fileName:string){preferences.removePreferencesFromCacheSync(context,fileName);this.dataPreferences=preferences.getPreferencesSync(context,{name:fileName});}

在创建实例前先从缓存中移除,确保获取的是最新实例。这是处理开发热重载时缓存残留的关键。

同步 API
this.dataPreferences.putSync(key,value);this.dataPreferences.flushSync();

使用同步 API 而非异步,简化调用方代码。对于轻量级数据,同步操作的性能影响可以忽略。

容错处理
publicput(key:string,value:object|string|number|boolean):void{if(!this.dataPreferences){Logger.info('PreferenceUtil: dataPreferences is null');return;}try{this.dataPreferences.putSync(key,value);this.dataPreferences.flushSync();}catch(e){Logger.error(`PreferenceUtil: put error:${JSON.stringify(e)}`);}}

所有操作都包含 try-catch,确保存储异常不会导致应用崩溃。

三、在 MainViewModel 中使用

3.1 初始化

// viewmodel/MainViewModel.etspublicasyncinit(context:Context):Promise<void>{this.prefUtil=PreferenceUtil.getInstance(context,AppConstants.PREF_FILE);this.loadDays();}

常量定义:

// common/Constants.etsexportclassAppConstants{publicstaticreadonlyPREF_FILE:string='important_days';publicstaticreadonlyPREF_KEY_DAYS:string='important_day_list';}

3.2 加载数据

privateloadDays():void{if(!this.prefUtil)return;constjsonStr=this.prefUtil.get(AppConstants.PREF_KEY_DAYS,'[]')asstring;try{constarr:string[]=JSON.parse(jsonStr);this.dayList=arr.map((json:string)=>ImportantDay.fromJSON(json)).filter((day:ImportantDay|null)=>day!==null)asImportantDay[];}catch(e){Logger.error(`MainViewModel: loadDays error:${JSON.stringify(e)}`);this.dayList=[];}}

3.3 保存数据

privatesaveDays():void{if(!this.prefUtil)return;constarr:string[]=this.dayList.map((day:ImportantDay)=>day.toJSON());this.prefUtil.put(AppConstants.PREF_KEY_DAYS,JSON.stringify(arr));}

3.4 数据存储格式

Preferences 中存储的数据结构:

文件: important_days 键: important_day_list 值: '["{\"id\":\"1690000000000_3456\",\"title\":\"妈妈生日\",\"date\":\"2026-03-15\",\"description\":\"记得买蛋糕\",\"colorIndex\":5,\"countMode\":0,\"repeatType\":1,\"advanceDays\":3,\"isPinned\":true,\"createdAt\":1690000000000,\"updatedAt\":1690000000000}"]'

外层是一个 JSON 数组字符串,每个元素是ImportantDay.toJSON()的结果。这种嵌套设计的原因:

  1. Preferences 的putSync只支持基本类型(string、number、boolean)和 string
  2. 将数组序列化为字符串存储,可以一次性保存所有数据
  3. 加载时一次性反序列化,减少 IO 操作

四、putSync 与 flushSync 的区别

this.dataPreferences.putSync(key,value);// 写入内存缓存this.dataPreferences.flushSync();// 持久化到文件
  • putSync:将数据写入内存缓存,速度极快
  • flushSync:将内存缓存的数据刷写到磁盘文件,确保数据持久化

两者必须配合使用。如果只调用putSync而不flushSync,数据在应用关闭后会丢失。

五、removePreferencesFromCacheSync 的作用

preferences.removePreferencesFromCacheSync(context,fileName);

这行代码在构造函数中调用,用于从内存缓存中移除指定文件的 Preferences 实例。这在以下场景中很重要:

  1. 开发阶段热重载:DevEco Studio 热重载时,旧的 Preferences 实例可能还留在缓存中
  2. 多实例场景:确保每次getInstance获取的是最新状态
  3. 数据同步:防止缓存中的脏数据影响新实例

六、存储方案扩展思考

6.1 当前方案的局限

  1. 全量保存:每次修改都保存整个列表,数据量大时可能有性能问题
  2. 无索引:查询某日的重要日需要遍历整个列表
  3. 无事务:多步操作不具备原子性

6.2 如果使用 RDB

如果未来数据量增长,可以迁移到关系型数据库:

CREATETABLEimportant_days(idTEXTPRIMARYKEY,titleTEXTNOTNULL,dateTEXTNOTNULL,descriptionTEXT,color_indexINTEGERDEFAULT0,count_modeINTEGERDEFAULT0,repeat_typeINTEGERDEFAULT0,advance_daysINTEGERDEFAULT0,is_pinnedINTEGERDEFAULT0,created_atINTEGER,updated_atINTEGER);CREATEINDEXidx_dateONimportant_days(date);CREATEINDEXidx_pinnedONimportant_days(is_pinned);

但当前 Preferences 方案对于这个应用规模完全够用,简单可靠。

七、小结

本篇详细介绍了 ImportantDays 项目的数据持久化方案。PreferenceUtil 通过单例模式封装 Preferences API,提供简洁的get/put/delete接口。MainViewModel 在每次 CRUD 操作后自动调用saveDays()持久化数据,确保数据不丢失。

http://www.jsqmd.com/news/1196876/

相关文章:

  • 给拿不定主意卖不卖的人的一张决策清单!2026 年 7月 - 回收测评
  • 2026年7月最新杭州卡地亚官方售后客服服务电话及地址网点大全 - 卡地亚官方售后中心
  • 北京欧米茄回收价格查询和各大平台实测排行(2026年7月最新) - 欧米茄官方服务中心
  • 长上下文序列并行训练深度解析:从 Ring Attention 到 Ulysses 的百万 token 分布式注意力架构
  • 帝舵中国官方售后服务中心|官方地址与客服热线权威信息通知(2026年7月更新) - 帝舵中国官方服务中心
  • 静态扭矩传感器信得过,广东犸力品质靠谱,以高精准度和高可靠性默默护航大国重器研发 - 品牌速递
  • 积家中国官方售后服务中心|服务热线及全部维修详细地址权威信息通知(2026年7月更新) - 积家官方售后服务中心
  • 个人Linux操作系统学习笔记9 - 孤儿进程与进程优先级
  • LMK61E0M DCXO实战:从I2C配置到环路滤波,打造70.656MHz高精度时钟
  • 计算机毕业设计之jsp学生党支部活动管理系统
  • 漯河2026年宣传片拍摄公司年度权威排名|头部机构深度评测AI视频制作剪辑包装一站式拍摄报价全流程选择指南深度解析 - 全国影像制作行业测评
  • HAM未来路线图:下一代高可用迁移技术的发展方向与展望
  • HarmonyOs应用《重要日》开发第7篇 - 日期处理:dayjs 集成与 DateUtil 封装
  • 计算机毕业设计之jsp学生宿舍管理系统
  • 数字IC验证面试核心考点深度剖析与实战应对
  • 宝珀中国官方售后服务中心|官方热线及网点地址权威信息公示(2026年7月最新) - 宝珀官方售后服务中心
  • Cursor AI布局切换实战手册(从VS Code迁移者必读):12个真实项目踩坑复盘与标准化配置模板
  • 2026年7月最新宁波天梭官方售后服务热线与网点地址查询 - 天梭服务中心
  • 流程引擎宕机后流程全丢?Spring Boot 工作流持久化恢复实战,让你的业务永不“断片”
  • MOS管从入门到实战:5分钟掌握核心原理与应用技巧
  • 【初阶·安全】大模型常见攻击向量认知深度解析:Prompt Injection、越狱攻击与数据投毒的攻防全景
  • 44.嵌入式C语言工程实践:最危险的bug——代码能跑,但逻辑已经错了
  • 2026年华东地区芝麻灰路沿石订购靠谱厂家联系方式汇总 - 热点品牌推荐
  • 安徽周边采购水平移载装箱机筛选靠谱供应厂家品牌 - 热点品牌推荐
  • Python实现跨境电商商品图批量翻译教程
  • 天津积家回收价格查询和靠谱回收平台实测排行(2026年7月最新) - 积家官方售后服务中心
  • JDK 11 新特性详解
  • 实战:在CentOS7上绕过GLIBC限制,安全运行高版本Node.js
  • 叠石桥墙内水管漏水检测维修师傅电话及优质服务商家盘点 - 热点品牌推荐
  • 深信服防火墙开局+访问控制策略+SNAT+DNAT+策略路由配置