HarmonyOS鸿蒙Next中如何使用Preferences实现轻量级数据存储?

HarmonyOS鸿蒙Next中如何使用Preferences实现轻量级数据存储? 1.如何使用鸿蒙Preferences实现轻量级数据存储? 2. 如何处理复杂对象的序列化?

4 回复

【背景知识】

[@ohos.data.preferences (用户首选项)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-preferences):

用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。

数据存储采用键值对形式,键为字符串类型,值可为数字、字符、布尔类型及其对应的数组。

用户首选项的持久化文件存储在preferencesDir路径下,创建preferences对象前,需要保证preferencesDir路径可读写。持久化文件存储路径中的加密等级会影响文件的可读写状态,路径访问限制详见应用文件目录与应用文件路径

【参考方案】

  1. 可参考字体大小调节示例,通过滑动组件Slider与[@ohos.data.preferences](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-preferences)实现应用内的字体大小调节,并实现屏蔽系统字体大小设置功能,用户可以按需调整当前阅读内容字体大小。
// 在首选项中设置字体大小
saveChangeFontSize(fontSize: number) {
  try {
    let fontPreferences: preferences.Preferences = this.getFontPreferences();
    fontPreferences.putSync(KEY_APP_FONT_SIZE, fontSize);
    fontPreferences.flushSync();
  } catch (err) {
    // ...
  }
}
// 从首选项中读取字体大小
getChangeFontSize(): number {
  let defaultFontSize: number = 0;
  let fontPreferences: preferences.Preferences = this.getFontPreferences();
  let fontSize: number = fontPreferences.getSync(KEY_APP_FONT_SIZE, defaultFontSize) as number;
  return fontSize;
}
  1. 使用Slider组件实现字体大小的滑动选择,每次滑动后调用[@ohos.data.preferences](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-preferences)对最新字体大小进行存储。
Slider({
  value: this.changeFontSize === CommonConstants.SET_SIZE_HUGE
    ? CommonConstants.SET_SLIDER_MAX : this.changeFontSize,
  min: CommonConstants.SET_SLIDER_MIN, // 滑动最小值
  max: CommonConstants.SET_SLIDER_MAX, // 滑动最大值
  step: CommonConstants.SET_SLIDER_STEP, // 步长
  // 其他设置
})
  .onChange((value: number) => {
    if (this.changeFontSize === 0) {
      this.changeFontSize = PreferencesUtil.getChangeFontSize();
      return;
    }
    this.changeFontSize = value
    PreferencesUtil.saveChangeFontSize(this.changeFontSize);
    this.changeFontSize = PreferencesUtil.getChangeFontSize()
  })

更多关于HarmonyOS鸿蒙Next中如何使用Preferences实现轻量级数据存储?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


实现代码

import { preferences } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';

/**
 * 应用设置模型
 */
export interface AppSettings {
  theme: string; // 主题模式
  themeColorId: string; // 主题色ID
  fontSize: string; // 字体大小
  viewMode: string; // 视图模式
  defaultRecordType: string; // 默认记录类型
  customAmounts: number[]; // 自定义快捷金额
  customEventTypes: string[]; // 自定义事件类型
  reciprocationGrowthRate: number; // 待回礼增长比例
  autoBackup: boolean; // 自动备份开关
  autoBackupFrequency: string; // 备份频率
  maxBackupCount: number; // 最大备份数
  lastBackupTime: number; // 最后备份时间
  biometricAuth: boolean; // 生物识别认证
  notificationEnabled: boolean; // 通知开关
  syncEnabled: boolean; // 同步开关
}

/**
 * Preferences存储服务
 */
export class PreferencesService {
  private static instance: PreferencesService;
  private preferencesStore: preferences.Preferences | null = null;
  private readonly STORE_NAME = 'app_settings';

  private constructor() {}

  static getInstance(): PreferencesService {
    if (!PreferencesService.instance) {
      PreferencesService.instance = new PreferencesService();
    }
    return PreferencesService.instance;
  }

  /**
   * 初始化Preferences
   */
  async init(context: common.UIAbilityContext): Promise<void> {
    try {
      this.preferencesStore = await preferences.getPreferences(
        context,
        this.STORE_NAME
      );
      console.info('Preferences初始化成功');
    } catch (err) {
      console.error('Preferences初始化失败:', JSON.stringify(err));
      throw new Error('Preferences初始化失败');
    }
  }

  /**
   * 保存应用设置
   */
  async saveSettings(settings: AppSettings): Promise<void> {
    try {
      if (!this.preferencesStore) {
        throw new Error('Preferences未初始化');
      }

      // 将对象序列化为JSON字符串
      const settingsJson = JSON.stringify(settings);

      // 保存到Preferences
      await this.preferencesStore.put('settings', settingsJson);

      // 立即持久化到磁盘
      await this.preferencesStore.flush();

      console.info('设置保存成功');
    } catch (err) {
      console.error('保存设置失败:', JSON.stringify(err));
      throw new Error('保存设置失败');
    }
  }

  /**
   * 加载应用设置
   */
  async loadSettings(): Promise<AppSettings> {
    try {
      if (!this.preferencesStore) {
        throw new Error('Preferences未初始化');
      }

      // 从Preferences读取
      const settingsJson = await this.preferencesStore.get('settings', '');

      if (!settingsJson) {
        // 返回默认设置
        return this.getDefaultSettings();
      }

      // 反序列化JSON
      const settings = JSON.parse(settingsJson as string) as AppSettings;
      return settings;

    } catch (err) {
      console.error('加载设置失败:', JSON.stringify(err));
      return this.getDefaultSettings();
    }
  }

  /**
   * 获取默认设置
   */
  private getDefaultSettings(): AppSettings {
    return {
      theme: 'auto',
      themeColorId: 'orange',
      fontSize: 'medium',
      viewMode: 'list',
      defaultRecordType: 'sent',
      customAmounts: [100, 200, 500, 600, 1000],
      customEventTypes: [],
      reciprocationGrowthRate: 1.0,
      autoBackup: true,
      autoBackupFrequency: 'daily',
      maxBackupCount: 7,
      lastBackupTime: 0,
      biometricAuth: false,
      notificationEnabled: true,
      syncEnabled: false
    };
  }

  /**
   * 更新单个设置项
   */
  async updateSetting<K extends keyof AppSettings>(
    key: K,
    value: AppSettings[K]
  ): Promise<void> {
    try {
      const settings = await this.loadSettings();
      settings[key] = value;
      await this.saveSettings(settings);
    } catch (err) {
      console.error('更新设置失败:', JSON.stringify(err));
      throw new Error('更新设置失败');
    }
  }

  /**
   * 获取单个设置项
   */
  async getSetting<K extends keyof AppSettings>(
    key: K
  ): Promise<AppSettings[K]> {
    const settings = await this.loadSettings();
    return settings[key];
  }

  /**
   * 删除所有设置(恢复默认)
   */
  async resetSettings(): Promise<void> {
    try {
      if (!this.preferencesStore) {
        throw new Error('Preferences未初始化');
      }

      await this.preferencesStore.delete('settings');
      await this.preferencesStore.flush();

      console.info('设置已重置');
    } catch (err) {
      console.error('重置设置失败:', JSON.stringify(err));
      throw new Error('重置设置失败');
    }
  }

  /**
   * 保存简单键值对
   */
  async put(key: string, value: string | number | boolean): Promise<void> {
    try {
      if (!this.preferencesStore) {
        throw new Error('Preferences未初始化');
      }

      await this.preferencesStore.put(key, value);
      await this.preferencesStore.flush();
    } catch (err) {
      console.error('保存数据失败:', JSON.stringify(err));
      throw new Error('保存数据失败');
    }
  }

  /**
   * 获取简单键值对
   */
  async get(key: string, defaultValue: string | number | boolean): Promise<string | number | boolean> {
    try {
      if (!this.preferencesStore) {
        throw new Error('Preferences未初始化');
      }

      return await this.preferencesStore.get(key, defaultValue);
    } catch (err) {
      console.error('获取数据失败:', JSON.stringify(err));
      return defaultValue;
    }
  }
}

使用示例

// 在EntryAbility中初始化
export default class EntryAbility extends UIAbility {
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    const prefsService = PreferencesService.getInstance();
    await prefsService.init(this.context);

    // 加载设置
    const settings = await prefsService.loadSettings();
    console.log('当前主题:', settings.theme);
  }
}

// 在设置页面中使用
@Entry
@Component
struct SettingsPage {
  @State settings: AppSettings = {} as AppSettings;
  private prefsService = PreferencesService.getInstance();

  async aboutToAppear() {
    this.settings = await this.prefsService.loadSettings();
  }

  // 切换主题
  async onThemeChange(theme: string) {
    await this.prefsService.updateSetting('theme', theme);
    this.settings.theme = theme;
  }

  // 更新快捷金额
  async onCustomAmountsChange(amounts: number[]) {
    await this.prefsService.updateSetting('customAmounts', amounts);
    this.settings.customAmounts = amounts;
  }

  build() {
    Column() {
      Text(`当前主题: ${this.settings.theme}`)
      Text(`快捷金额: ${this.settings.customAmounts.join(', ')}`)
    }
  }
}

原理解析

1. Preferences初始化

preferences.getPreferences(context, 'app_settings')
  • 需要传入UIAbilityContext
  • 指定存储名称(文件名)
  • 返回Preferences实例

2. 复杂对象序列化

JSON.stringify(settings) // 保存时
JSON.parse(settingsJson) // 读取时
  • Preferences只支持基本类型
  • 复杂对象需要JSON序列化
  • 注意类型转换

3. 立即持久化

await this.preferencesStore.flush();
  • put()只是写入内存
  • flush()才会持久化到磁盘
  • 确保数据不丢失

4. 泛型类型安全

async updateSetting<K extends keyof AppSettings>(
  key: K,
  value: AppSettings[K]
)
  • 使用泛型约束key
  • 保证value类型正确
  • 编译时类型检查

最佳实践

  1. 单例模式: 全局只有一个Preferences实例
  2. 默认值: 读取失败时返回默认设置
  3. 立即持久化: 重要数据修改后立即flush()
  4. 异常处理: 所有操作都要try-catch
  5. 类型安全: 使用TypeScript接口定义数据结构

避坑指南

  1. 未初始化: 使用前必须调用init()
  2. 忘记flush: 不flush数据不会保存到磁盘
  3. 类型转换: get()返回的是联合类型,需要类型断言
  4. 并发问题: 多处同时修改可能导致数据覆盖
  5. 存储大小: Preferences适合小数据,大数据用数据库

效果展示

存储的数据文件位置:

/data/storage/el2/base/preferences/app_settings

数据格式:

{
  "settings": "{\"theme\":\"auto\",\"themeColorId\":\"orange\",\"fontSize\":\"medium\",\"customAmounts\":[100,200,500,600,1000]}"
}

在HarmonyOS Next中,使用Preferences进行轻量级数据存储的步骤如下:

  1. 导入模块:import { preferences } from '@kit.ArkData'
  2. 获取Preferences实例:通过preferences.getPreferences(context, 'YourFileName')获取。
  3. 存储数据:使用put()方法,如preferences.put('key', value),然后调用flush()保存。
  4. 读取数据:使用get()方法,如preferences.get('key', defaultValue)
  5. 删除数据:使用delete()方法,如preferences.delete('key')
  6. 数据持久化:所有操作后需调用flush()flushSync()同步到文件。

在HarmonyOS Next中,使用Preferences进行轻量级数据存储主要涉及以下步骤:

  1. 获取Preferences实例:通过PreferencesHelper.getPreferences()方法获取指定文件名的Preferences对象,用于后续的数据读写操作。

  2. 数据存储:使用putString()putInt()putBoolean()等方法存储基本数据类型。所有修改需通过flush()commit()提交到文件。

  3. 数据读取:通过getString()getInt()getBoolean()等方法读取已存储的数据,可设置默认值。

对于复杂对象的序列化处理:

  • 将对象转换为JSON字符串(如使用JSON.stringify()),再以字符串形式存储到Preferences。
  • 读取时获取JSON字符串,通过反序列化(如JSON.parse())还原为对象。

注意事项:

  • Preferences适用于少量键值对数据,大量数据建议使用关系型数据库。
  • 存储复杂对象时,需确保对象可序列化,避免循环引用。
  • 异步操作推荐使用flush(),同步操作使用commit()
回到顶部