033-Preferences键值存储
Preferences 键值存储 — 轻量级配置数据的持久化方案
一、引言
在应用开发中,大量的配置类数据(如用户偏好、开关状态、简单缓存)不需要复杂的数据库支持,只需要一个轻量级的键值存储方案。鸿蒙系统的 Preferences(首选项)正是为此类场景设计,它以简洁的 API、异步的操作和良好的性能,成为配置数据持久化的首选方案。MoneyTrack 在提醒设置、预算金额等场景中大量使用 Preferences,实现了配置数据的快速读写和持久化。
二、核心知识点
2.1 Preferences 完整使用步骤
Preferences 的使用遵循「获取实例 → 读写数据 → 刷入磁盘」的标准流程:
import{preferences}from'@kit.ArkData'asyncfunctiondemoPreferences(context:Context){// 第一步:获取 Preferences 实例// getPreferences 是异步操作,返回 Preferences 对象// 文件名 'demo_pref' 对应磁盘上的一个 XML 文件constpref=awaitpreferences.getPreferences(context,'demo_pref')// 第二步:写入数据(修改在内存中,尚未落盘)awaitpref.put('username','alice')// stringawaitpref.put('age',25)// numberawaitpref.put('is_vip',true)// booleanawaitpref.put('tags',['premium','yearly'])// Array<string>// 第三步:刷入磁盘(必须显式调用 flush)// flush 将内存中的所有修改批量写入磁盘文件awaitpref.flush()// 读取数据(需指定默认值,防止首次读取返回 undefined)constusername=awaitpref.get('username','default_user')constage=awaitpref.get('age',18)constisVip=awaitpref.get('is_vip',false)}关于 flush 的说明:flush()是异步批量写入操作,调用后不保证立即完成。在应用进入后台或即将退出前调用flush()可确保数据安全。
2.2 异步读写流程
2.3 多实例隔离
Preferences 支持基于文件名创建多个独立实例,不同模块使用不同的文件,实现数据隔离:
// 不同模块使用独立的 Preferences 文件constreminderPref=awaitpreferences.getPreferences(context,'reminder_settings')constthemePref=awaitpreferences.getPreferences(context,'theme_settings')constbudgetPref=awaitpreferences.getPreferences(context,'budget_settings')// 每个文件独立存储,互不干扰awaitreminderPref.put('enabled',true)awaitthemePref.put('theme','dark')awaitbudgetPref.put('monthly_limit',5000)多实例隔离的好处:
- 模块间数据解耦,修改一个模块的配置不影响其他模块
- 文件粒度的管理,方便单独清除某个模块的缓存
- 减少单个文件大小,提升读写性能
2.4 数据删除操作
除了读写,Preferences 还提供了数据删除能力:
// 删除指定的 key-valueawaitpref.delete('temp_setting')// 检查某个 key 是否存在consthasKey=awaitpref.has('reminder_enabled')// 清空所有数据(谨慎使用)awaitpref.clear()// 删除 Preferences 文件(释放磁盘空间)awaitpreferences.deletePreferences(context,'obsolete_settings')// 注意:delete 和 clear 后都需要 flush 才能持久化awaitpref.flush()2.5 数据变更监听
Preferences 支持通过on('change')事件监听数据变更,这在多页面共享同一配置时特别有用——当某个页面修改了配置,其他页面可以实时感知变化并更新 UI:
preferences.on('change',(key)=>{if(key==='reminder_enabled'){this.refreshUI()}})三、项目代码案例
3.1 ReminderSettingVM 完整实现
在products/entry/src/main/ets/viewmodel/reminder/ReminderSettingVM.ets中,ReminderSettingVM 封装了提醒设置的全部逻辑:
exportclassReminderSettingVM{privatepref:preferences.Preferences|null=nullprivatecontext:Context// 持久化键值privatestaticreadonlyKEY_ENABLED='reminder_enabled'privatestaticreadonlyKEY_TIME='reminder_time'privatestaticreadonlyKEY_PERIOD='reminder_period'privatestaticreadonlyPREF_NAME='reminder_settings'constructor(context:Context){this.context=context}// 初始化:加载偏好设置asyncinit():Promise<void>{this.pref=awaitpreferences.getPreferences(this.context,ReminderSettingVM.PREF_NAME)// 注册监听this.pref.on('change',(key)=>{console.log('提醒设置变更:',key)})}// 读取提醒是否开启asyncisEnabled():Promise<boolean>{returnawaitthis.pref!.get(ReminderSettingVM.KEY_ENABLED,false)}// 设置提醒开关asyncsetEnabled(enabled:boolean):Promise<void>{awaitthis.pref!.put(ReminderSettingVM.KEY_ENABLED,enabled)awaitthis.pref!.flush()}// 读取提醒时间asyncgetReminderTime():Promise<string>{returnawaitthis.pref!.get(ReminderSettingVM.KEY_TIME,'08:00')}// 设置提醒时间asyncsetReminderTime(time:string):Promise<void>{awaitthis.pref!.put(ReminderSettingVM.KEY_TIME,time)awaitthis.pref!.flush()}}3.2 预算金额持久化
在预算管理模块中,月度预算金额通过 Preferences 持久化:
// 键:budget_{year}_{month},支持分别存储每个月的预算asyncsaveMonthlyBudget(year:number,month:number,amount:number){constpref=awaitpreferences.getPreferences(this.context,'budget_settings')constkey=`budget_${year}_${month}`awaitpref.put(key,amount)awaitpref.flush()}asyncloadMonthlyBudget(year:number,month:number):Promise<number>{constpref=awaitpreferences.getPreferences(this.context,'budget_settings')constkey=`budget_${year}_${month}`returnawaitpref.get(key,0)}3.3 使用注意事项
- 及时 flush:修改后调用
flush()确保数据落盘,避免应用退出导致数据丢失 - 默认值:使用
get()方法时始终提供合理的默认值,处理首次使用场景 - 键名规范:使用有意义的键名,如
module_key的命名模式 - 数据量控制:Preferences 适合少量配置数据(建议不超过 100 个 key),大量数据应使用 RDB
- 避免频繁 flush:多次 put 后统一 flush 一次,减少磁盘 I/O
四、参考文档
- Preferences 首选项指南
- Preferences API 参考
- 数据存储安全建议
