HarmonyOs应用《重要日》开发第6篇 - 数据持久化存储 本篇深入剖析 ImportantDays 项目的数据持久化方案——基于 HarmonyOS ArkData 模块的 Preferences 轻量级存储以及 PreferenceUtil 工具类的单例封装。一、HarmonyOS 数据存储方案对比HarmonyOS 提供了多种数据存储方案方案适用场景特点Preferences轻量级 KV 数据同步操作适合少量数据关系型数据库 (RDB)结构化数据SQLite支持复杂查询分布式数据 (DDS)跨设备同步多设备数据一致性文件存储大文件直接读写文件系统ImportantDays 选择 Preferences 的原因数据量小通常几十条重要日数据结构简单JSON 字符串不需要复杂查询同步操作代码简洁二、PreferenceUtil 工具类2.1 完整实现// utils/PreferenceUtil.etsimport{preferences}fromkit.ArkData;import{common}fromkit.AbilityKit;import{Logger}from./Logger;exportclassPreferenceUtil{privatestaticpreferenceRecord:Mapstring,PreferenceUtilnewMap();privatedataPreferences:preferences.Preferences|nullnull;privateconstructor(context:Context,fileName:string){preferences.removePreferencesFromCacheSync(context,fileName);this.dataPreferencespreferences.getPreferencesSync(context,{name:fileName});Logger.info(PreferenceUtil:${fileName}init:${this.dataPreferences!null});}publicstaticgetInstance(context?:Context,fileName:stringdefault):PreferenceUtil{if(PreferenceUtil.preferenceRecord.has(fileName)){returnPreferenceUtil.preferenceRecord.get(fileName)!;}constctx:Contextcontext||getContext()ascommon.UIAbilityContext;constpreferenceUtil:PreferenceUtilnewPreferenceUtil(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:Mapstring,PreferenceUtilnewMap();publicstaticgetInstance(context?:Context,fileName:stringdefault):PreferenceUtil{if(PreferenceUtil.preferenceRecord.has(fileName)){returnPreferenceUtil.preferenceRecord.get(fileName)!;}// ...创建新实例}使用Map按fileName维护多个实例每个文件对应一个PreferenceUtil实例。缓存清理privateconstructor(context:Context,fileName:string){preferences.removePreferencesFromCacheSync(context,fileName);this.dataPreferencespreferences.getPreferencesSync(context,{name:fileName});}在创建实例前先从缓存中移除确保获取的是最新实例。这是处理开发热重载时缓存残留的关键。同步 APIthis.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):Promisevoid{this.prefUtilPreferenceUtil.getInstance(context,AppConstants.PREF_FILE);this.loadDays();}常量定义// common/Constants.etsexportclassAppConstants{publicstaticreadonlyPREF_FILE:stringimportant_days;publicstaticreadonlyPREF_KEY_DAYS:stringimportant_day_list;}3.2 加载数据privateloadDays():void{if(!this.prefUtil)return;constjsonStrthis.prefUtil.get(AppConstants.PREF_KEY_DAYS,[])asstring;try{constarr:string[]JSON.parse(jsonStr);this.dayListarr.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()的结果。这种嵌套设计的原因Preferences 的putSync只支持基本类型string、number、boolean和 string将数组序列化为字符串存储可以一次性保存所有数据加载时一次性反序列化减少 IO 操作四、putSync 与 flushSync 的区别this.dataPreferences.putSync(key,value);// 写入内存缓存this.dataPreferences.flushSync();// 持久化到文件putSync将数据写入内存缓存速度极快flushSync将内存缓存的数据刷写到磁盘文件确保数据持久化两者必须配合使用。如果只调用putSync而不flushSync数据在应用关闭后会丢失。五、removePreferencesFromCacheSync 的作用preferences.removePreferencesFromCacheSync(context,fileName);这行代码在构造函数中调用用于从内存缓存中移除指定文件的 Preferences 实例。这在以下场景中很重要开发阶段热重载DevEco Studio 热重载时旧的 Preferences 实例可能还留在缓存中多实例场景确保每次getInstance获取的是最新状态数据同步防止缓存中的脏数据影响新实例六、存储方案扩展思考6.1 当前方案的局限全量保存每次修改都保存整个列表数据量大时可能有性能问题无索引查询某日的重要日需要遍历整个列表无事务多步操作不具备原子性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()持久化数据确保数据不丢失。

相关新闻

最新新闻

树莓派无线网络配置:从开机自连到静态IP设置的完整避坑指南

树莓派无线网络配置:从开机自连到静态IP设置的完整避坑指南

1. 树莓派无线网络配置入门指南刚拿到树莓派的新手最常遇到的第一个难题就是如何让这个小家伙连上WiFi。你可能已经按照网上的教程配置了wpa_supplicant.conf文件,重启后发现树莓派还是无法自动连接无线网络。这种情况我见过太多,通常不是你的操作有问题…

2026/7/16 1:25:17
C/C++启动外部进程:从ShellExecute到CreateProcess的深度对比与实战选型

C/C++启动外部进程:从ShellExecute到CreateProcess的深度对比与实战选型

1. Windows进程启动API全景概览在Windows平台开发中,经常需要从C/C程序中启动外部可执行程序。比如你的程序需要调用系统计算器、打开文本文件,或者启动另一个自己开发的辅助工具。Windows提供了四种主要的API来实现这个功能:ShellExecute、W…

2026/7/16 1:25:17
开关电源之光耦隔离反馈:从原理到实战参数设计

开关电源之光耦隔离反馈:从原理到实战参数设计

1. 光耦隔离反馈的核心价值在反激式开关电源设计中,原副边隔离是保证系统安全的关键屏障。去年我参与的一款医疗设备电源项目就曾因隔离失效导致整批产品召回,这个教训让我深刻认识到光耦隔离的重要性。光耦隔离反馈电路就像电源系统的"神经传导系统…

2026/7/16 1:25:17
你的Mac空间总是不够用?Pearcleaner帮你彻底解决应用残留问题

你的Mac空间总是不够用?Pearcleaner帮你彻底解决应用残留问题

你的Mac空间总是不够用?Pearcleaner帮你彻底解决应用残留问题 【免费下载链接】Pearcleaner A free, source-available and fair-code licensed mac app cleaner 项目地址: https://gitcode.com/gh_mirrors/pe/Pearcleaner 你是否经常遇到这样的困扰&#xf…

2026/7/16 1:25:17
从VCC、VDD到VSS:电子设计中的电源符号命名逻辑与实战解析

从VCC、VDD到VSS:电子设计中的电源符号命名逻辑与实战解析

1. 电源符号的起源与晶体管技术渊源第一次看到芯片数据手册上密密麻麻的VCC、VDD、VSS符号时,我也曾一头雾水。直到有次把5V电源误接到3.3V的VDD引脚上,芯片冒烟的那一刻才深刻理解——这些符号背后藏着电子设计的基因密码。双字母重复的命名方式其实源自…

2026/7/16 1:25:17
STM32 Cube ADC 实现土壤湿度传感器的精准校准与滤波

STM32 Cube ADC 实现土壤湿度传感器的精准校准与滤波

1. 土壤湿度检测的挑战与解决方案在智能农业和家庭园艺场景中,土壤湿度检测是个看似简单却暗藏玄机的技术活。我刚开始接触这个领域时,以为插个传感器就能得到准确数据,结果发现同一盆花连续测三次能给出三个完全不同的数值。这种波动主要来自…

2026/7/16 1:20:15

月新闻