HarmonyOS 应用开发《掌上英语》第25篇:Preferences 键值存储——PreferenceUtil 封装深度解析 📅 2026/7/22 1:33:04 Preferences 键值存储——PreferenceUtil 封装深度解析引言在 HarmonyOS 应用开发中数据持久化是最基础也最核心的需求之一。kit.ArkData提供的 Preferences API 是一种轻量级键值存储方案适合存储配置项、用户偏好等小规模结构化数据。然而直接使用 Preferences API 存在几个痛点每次使用都需要获取Preferences实例、没有缓存机制导致重复创建、同步/异步 API 混用容易出错。本文将以PreferenceUtil工具类的完整实现为例深入剖析如何优雅地封装 Preferences 存储层。一、设计目标PreferenceUtil的设计围绕三个核心目标展开目标说明单例复用同一文件只创建一个 Preferences 实例避免重复 I/O文件隔离支持按文件名隔离不同业务的数据域同步简洁提供putSync/getSync等同步 API简化调用链路二、架构设计单例 Map 多实例缓存PreferenceUtil最巧妙的设计在于它结合了单例模式和Map 缓存import{preferences}fromkit.ArkData;import{Logger}from./Logger;import{GlobalContextUtils}from./ContextUtils;exportclassPreferenceUtil{privatestaticpreferenceRecord:Mapstring,PreferenceUtilnewMap();privatedataPreferences:preferences.Preferences|nullnull;privateconstructor(context:Context,fileName:string){try{// 先清除缓存确保获取最新数据preferences.removePreferencesFromCacheSync(context,fileName);this.dataPreferencespreferences.getPreferencesSync(context,{name:fileName});Logger.info(PreferenceUtil:${fileName}init:${this.dataPreferences!null});}catch(e){Logger.error(PreferenceUtil error :${JSON.stringify(e)});}}publicstaticgetInstance(fileName:stringdefault){if(PreferenceUtil.preferenceRecord.has(fileName)){returnPreferenceUtil.preferenceRecord.get(fileName)!!;}letpreferenceUtilnewPreferenceUtil(GlobalContextUtils.globalContext,fileName);PreferenceUtil.preferenceRecord.set(fileName,preferenceUtil);returnpreferenceUtil;}// ...}设计要点分析构造函数私有化外部无法直接new PreferenceUtil()强制通过getInstance获取实例。Map 缓存多实例preferenceRecord以文件名fileName为键每个文件对应一个PreferenceUtil实例。这意味着生词本、学习计划、统计数据等不同业务模块可以使用独立的存储文件。缓存清除策略构造函数中先调用removePreferencesFromCacheSync清除框架层缓存确保获取到磁盘上的最新数据。三、核心读写操作3.1 写入数据publicput(key:string,value:preferences.ValueType){if(!this.dataPreferences){Logger.info(PreferenceUtil: dataPreferences is null);}try{this.dataPreferences?.putSync(key,value);this.dataPreferences?.flush();Logger.info(PreferenceUtil: put:${key}${JSON.stringify(value)});}catch(e){Logger.info(PreferenceUtil: put error:${JSON.stringify(e)});}}关键点putSync同步写入与异步put相比同步 API 在写入后可以立即读取到结果。flush强制持久化Preferences 的写入默认是异步刷盘的调用flush()可以立即将数据写入磁盘防止应用异常退出导致数据丢失。ValueType支持支持string、number、boolean、Array、Object等多种类型。3.2 读取数据publicget(key:string,defaultValue?:preferences.ValueType){try{letdatathis.dataPreferences?.getSync(key,defaultValue);Logger.info(PreferenceUtil: get:${key}${JSON.stringify(data)});returndata;}catch(e){Logger.info(PreferenceUtil: get error:${JSON.stringify(e)});returndefaultValue;}}读取操作同样采用同步 API。defaultValue参数的设计让调用方可以更安全地处理键不存在的情况。3.3 其他操作// 检查键是否存在publichasSync(key:string):boolean{if(!this.dataPreferences)returnfalse;try{returnthis.dataPreferences?.hasSync(key);}catch(e){Logger.error(PreferenceUtil: hasSync error:${JSON.stringify(e)});}returnfalse;}// 获取所有键值对publicgetAllSync():object|null{if(!this.dataPreferences)returnnull;try{returnthis.dataPreferences.getAllSync();}catch(e){Logger.error(PreferenceUtil: getAllSync error:${JSON.stringify(e)});}returnnull;}// 删除指定键publicdelete(key:string){if(!this.dataPreferences)return;try{this.dataPreferences?.deleteSync(key);this.dataPreferences?.flush();}catch(e){Logger.info(testTag,删除失败JSON.stringify(e));}}// 清空所有数据publicclear(){if(!this.dataPreferences)return;try{this.dataPreferences.clearSync();this.dataPreferences?.flush();}catch(e){Logger.info(testTag,clear失败JSON.stringify(e));}}四、GlobalContextUtils 的作用PreferenceUtil在构造函数中需要Context参数而Context对象通常在 UIAbility 中才能获取到。项目通过GlobalContextUtils全局持有 ContextexportclassContextUtils{privatecontextMap:Mapstring,UIContextnewMap();privateuiContext:UIContext|undefinedundefined;privatecontext:Context|undefinedundefined;publicsetUIContext(abilityName:string,uiContext:UIContext){this.contextMap.set(abilityName,uiContext);this.setActiveContext(abilityName);}publicgetglobalContext(){returnthis.contextasContext;}}constinstancenewContextUtils();export{instanceasGlobalContextUtils};在 UIAbility 的onCreate或onWindowStageCreate中初始化后PreferenceUtil便可通过GlobalContextUtils.globalContext获取 Context无需层层传递。五、最佳实践5.1 合理划分存储文件不要把所有数据都塞进一个文件。建议按业务模块划分不同的fileName// 生词本存储PreferenceUtil.getInstance(new_words).put(key,value);// 学习计划存储PreferenceUtil.getInstance(learning_plan).put(key,value);// 通用配置PreferenceUtil.getInstance(default).put(key,value);5.2 每次写入都 flush虽然频繁调用flush()会有一定的性能开销但在关键数据写入后调用flush()可以极大降低数据丢失风险publicput(key:string,value:preferences.ValueType){this.dataPreferences?.putSync(key,value);this.dataPreferences?.flush();// 确保持久化}5.3 异常处理不可省略Preferences 操作可能因磁盘空间不足、文件损坏等原因抛出异常。所有 public 方法都应包含 try-catchtry{this.dataPreferences?.putSync(key,value);this.dataPreferences?.flush();}catch(e){Logger.error(PreferenceUtil: put error:${JSON.stringify(e)});}六、总结PreferenceUtil是一个典型的基础设施层封装它将 HarmonyOS Preferences API 的复杂性隐藏在简洁的接口之下。通过单例 Map 缓存设计实现了多文件隔离和实例复用通过同步 API flush 机制兼顾了使用便捷性和数据安全性。这种封装模式值得在 HarmonyOS 应用中推广它让上层业务代码可以专注于数据逻辑本身而无需关心底层存储细节。在后续的文章中我们将看到各个 ManagerNewWordManager、StatisticsManager、LearningPlanManager如何基于PreferenceUtil构建各自的持久化能力。