1. HarmonyOS preferences存储

  参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkdata-V5

1.1. 说明

  应用数据持久化,是指应用将内存中的数据通过文件或数据库的形式保存到设备上。内存中的数据形态通常是任意的数据结构或数据对象,存储介质上的数据形态可能是文本、数据库、二进制文件等。
  HarmonyOS标准系统支持典型的存储数据形态,包括用户首选项、键值型数据库、关系型数据库。
  开发者可以根据如下功能介绍,选择合适的数据形态以满足自己应用数据的持久化需要。
  (1)用户首选项(Preferences):通常用于保存应用的配置信息。数据通过文本的形式保存在设备中,应用使用过程中会将文本中的数据全量加载到内存中,所以访问速度快、效率高,但不适合需要存储大量数据的场景。
  用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。当用户希望有一个全局唯一存储的地方,可以采用用户首选项来进行存储。Preferences会将该数据缓存在内存中,当用户读取的时候,能够快速从内存中获取数据。Preferences会随着存放的数据量越多而导致应用占用的内存越大,可使用键值型数据库----单版本数据库,针对每条记录,Key的长度≤1 KB,Value的长度<4 MB。因此,Preferences不适合存放过多的数据,适用的场景一般为应用保存用户的个性化设置(字体大小,是否开启夜间模式)等。
  (2)键值型数据库(KV-Store):一种非关系型数据库,其数据以“键值”对的形式进行组织、索引和存储,其中“键”作为唯一标识符。适合很少数据关系和业务关系的业务数据存储,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。
  (3)关系型数据库(RelationalStore):一种关系型数据库,以行和列的形式存储数据,广泛用于应用中的关系型数据的处理,包括一系列的增、删、改、查等接口,开发者也可以运行自己定义的SQL语句来满足复杂业务场景的需要。

1.2. 运作机制

  用户程序通过JS接口调用用户首选项读写对应的数据文件。开发者可以将用户首选项持久化文件的内容加载到Preferences实例,每个文件唯一对应到一个Preferences实例,系统会通过静态容器将该实例存储在内存中,直到主动从内存中移除该实例或者删除该文件。
HarmonyOS preferences存储-LMLPHP

1.3. 约束限制

  (1)Key键为string类型,要求非空且长度不超过80个字节。
  (2)如果Value值为string类型,请使用UTF-8编码格式,可以为空,不为空时长度不超过8192个字节。
  (3)内存会随着存储数据量的增大而增大,所以存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销。

1.4. 接口说明

1.5. 使用

1.5.1. 获取Preference

import { preferences } from '@kit.ArkData';
dataPreferences.getPreferences(this.context, "yuanzhen").then(preferences=>{
    }).catch((err:Error)=>{
    })

1.5.2. 写入数据

dataPreferences.getPreferences(this.context, "yuanzhen").then(preferences=>{
      //写入数据
      if(!preferences.has("yuanzhen")){
        preferences.put("yuanzhen","111")
      }
    }).catch((err:Error)=>{
    })

1.5.3. 读取数据

dataPreferences.getPreferences(this.context, "yuanzhen").then(preferences=>{
      //读取数据
      preferences.get("yuanzhen","默认数据").then((value)=>{
        hilog.info(0x0000, 'testTag', '读取数据成功: %{public}s', value);
      })
    }).catch((err:Error)=>{
    })

1.5.4.删除数据

dataPreferences.getPreferences(this.context, "yuanzhen").then(preferences=>{
      //删除数据
      preferences.delete("yuanzhen")
    }).catch((err:Error)=>{ 
    })

1.5.5.数据持久化

dataPreferences.getPreferences(this.context, "yuanzhen").then(preferences=>{
      //数据持久化
      preferences.flush()
    }).catch((err:Error)=>{
    })

1.5.6.删除指定文件

  注意:
  (1)调用该接口后,应用不允许再使用该Preferences实例进行数据操作,否则会出现数据一致性问题。
  (2)成功删除后,数据及文件将不可恢复。

dataPreferences.deletePreferences(this.context,"yuanzhen",(err:Error)=>{
})

1.6. 完整代码

HarmonyOS preferences存储-LMLPHP

1.2.1. PreferencesHelper

import preferences from '@ohos.data.preferences';
import dataPreferences from '@ohos.data.preferences';
import { AppUtil } from '../util/AppUtil';


/**
 *  Preferences工具类
 */
export class PreferencesHelper {
  private constructor() {}

  private static defaultPreferenceName: string = "SP_HARMONY_UTILS_PREFERENCES";
  private static preferences: preferences.Preferences;

  /**
   * 初始化
   * @param name Preferences实例的名称。
   */
  static init(preferenceName: string) {
    if (!PreferencesHelper.preferences || preferenceName !== PreferencesHelper.defaultPreferenceName) {
      PreferencesHelper.defaultPreferenceName = preferenceName;
      PreferencesHelper.preferences = dataPreferences.getPreferencesSync(AppUtil.getContext(), { name: preferenceName });
    }
  }
  /**
   * 获取Preferences实例
   * @param name
   * @returns
   */
  private static getPreferencesSync(preferenceName: string = PreferencesHelper.defaultPreferenceName): preferences.Preferences {
    if (preferenceName !== PreferencesHelper.defaultPreferenceName) {
      return dataPreferences.getPreferencesSync(AppUtil.getContext(), { name: preferenceName });
    } else if (!PreferencesHelper.preferences) {
      PreferencesHelper.preferences = dataPreferences.getPreferencesSync(AppUtil.getContext(), { name: preferenceName });
    }
    return PreferencesHelper.preferences;
  }

  /**
   * 获取Preferences实例
   * @param name
   * @returns
   */
  private static async getPreferences(preferenceName: string = PreferencesHelper.defaultPreferenceName): Promise<preferences.Preferences> {
    if (preferenceName !== PreferencesHelper.defaultPreferenceName) {
      return await dataPreferences.getPreferences(AppUtil.getContext(), preferenceName);
    } else if (!PreferencesHelper.preferences) {
      PreferencesHelper.preferences = await dataPreferences.getPreferences(AppUtil.getContext(), preferenceName);
    }
    return PreferencesHelper.preferences;
  }


  /**
   * 将数据缓存
   * @param key
   * @param value
   */
  static putSync(key: string, value: preferences.ValueType,
    preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = PreferencesHelper.getPreferencesSync(preferenceName); //获取实例
    preferences.putSync(key, value);
    preferences.flush(); //此处一定要flush,要不然不能永久序列化到本地
  }

  /**
   * 将数据缓存
   * @param key
   * @param value
   */
  static async put(key: string, value: preferences.ValueType,
    preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = await PreferencesHelper.getPreferences(preferenceName); //获取实例
    await preferences.put(key, value);
    await preferences.flush(); //此处一定要flush,要不然不能永久序列化到本地
  }


  /**
   * 获取缓存值
   * @param key
   * @param defValue
   * @returns
   */
  static getSync(key: string, defValue: preferences.ValueType,
    preferenceName: string = PreferencesHelper.defaultPreferenceName): preferences.ValueType {
    let preferences = PreferencesHelper.getPreferencesSync(preferenceName); //获取实例
    return preferences.getSync(key, defValue);
  }

  /**
   * 获取缓存值
   * @param key
   * @param defValue
   * @returns
   */
  static async get(key: string, defValue: preferences.ValueType,
    preferenceName: string = PreferencesHelper.defaultPreferenceName): Promise<preferences.ValueType> {
    let preferences = await PreferencesHelper.getPreferences(preferenceName); //获取实例
    return preferences.get(key, defValue);
  }


  /**
   * 获取string类型的缓存值
   * @param key
   * @returns
   */
  static getStringSync(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName): string {
    return PreferencesHelper.getSync(key, "", preferenceName) as string;
  }

  /**
   * 获取string类型的缓存值
   * @param key
   * @returns
   */
  static async getString(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName): Promise<string> {
    return (await PreferencesHelper.get(key, "", preferenceName)) as string;
  }


  /**
   * 获取number类型的缓存值
   * @param key
   * @returns
   */
  static getNumberSync(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName): number {
    return PreferencesHelper.getSync(key, 0, preferenceName) as number;
  }

  /**
   * 获取number类型的缓存值
   * @param key
   * @returns
   */
  static async getNumber(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName): Promise<number> {
    return (await PreferencesHelper.get(key, 0, preferenceName)) as number;
  }


  /**
   * 获取boolean类型的缓存值
   * @param key
   * @returns
   */
  static getBooleanSync(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName): boolean {
    return PreferencesHelper.getSync(key, false, preferenceName) as boolean;
  }

  /**
   * 获取boolean类型的缓存值
   * @param key
   * @returns
   */
  static async getBoolean(key: string,
    preferenceName: string = PreferencesHelper.defaultPreferenceName): Promise<boolean> {
    return (await PreferencesHelper.get(key, false, preferenceName)) as boolean;
  }


  /**
   * 检查缓存的Preferences实例中是否包含名为给定Key的存储键值对
   * @param key
   * @returns
   */
  static hasSync(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    return PreferencesHelper.getPreferencesSync(preferenceName).hasSync(key);
  }

  /**
   * 检查缓存的Preferences实例中是否包含名为给定Key的存储键值对
   * @param key
   * @returns
   */
  static async has(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = await PreferencesHelper.getPreferences(preferenceName); //获取实例
    return await preferences.has(key);
  }


  /**
   * 删除缓存值
   * @param key
   * @param preferenceName
   * @returns
   */
  static deleteSync(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = PreferencesHelper.getPreferencesSync(preferenceName); //获取实例
    preferences.deleteSync(key);
    preferences.flush() //此处一定要flush,要不然不能永久序列化到本地
  }

  /**
   * 删除缓存值
   * @param key
   * @param preferenceName
   * @returns
   */
  static async delete(key: string, preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = await PreferencesHelper.getPreferences(preferenceName); //获取实例
    await preferences.delete(key);
    return await preferences.flush() //此处一定要flush,要不然不能永久序列化到本地
  }


  /**
   * 清空缓存的Preferences实例中的所有数据
   * @param preferenceName
   * @returns
   */
  static clearSync(preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = PreferencesHelper.getPreferencesSync(preferenceName); //获取实例
    preferences.clearSync();
    preferences.flush() //此处一定要flush,要不然不能永久序列化到本地
  }

  /**
   * 清除缓存的Preferences实例中的所有数据
   * @param preferenceName
   * @returns
   */
  static async clear(preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = await PreferencesHelper.getPreferences(preferenceName); //获取实例
    await preferences.clear();
    return await preferences.flush() //此处一定要flush,要不然不能永久序列化到本地
  }


  /**
   * 从缓存中移出指定的Preferences实例,若Preferences实例有对应的持久化文件,则同时删除其持久化文件。
   * @param context
   * @param name Preferences 实例的名称。
   */
  static deletePreferences(context: Context, name: string) {
    dataPreferences.deletePreferences(context, name);
  }


  /**
   * 订阅数据变更,订阅的Key的值发生变更后,在执行flush方法后,触发callback回调。
   * @param callback 回调函数
   * @param preferenceName 实例的名称。
   */
  static onChange(callback: Callback<string>, preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = PreferencesHelper.getPreferencesSync(preferenceName); //获取实例
    preferences.on('change', callback);
  }

  /**
   * 取消订阅数据变更。
   * @param callback 需要取消的回调函数,不填写则全部取消。
   * @param preferenceName 实例的名称。
   */
  static offChange(callback?: Callback<string>, preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = PreferencesHelper.getPreferencesSync(preferenceName); //获取实例
    if (callback) {
      preferences.off('change', callback);
    } else {
      preferences.off('change');
    }
  }


  /**
   * 精确订阅数据变更,只有被订阅的key值发生变更后,在执行flush方法后,触发callback回调。
   * @param keys 需要订阅的key集合。
   * @param callback 回调函数
   * @param preferenceName 实例的名称。
   */
  static onDataChange(keys: string | Array<string>, callback: Callback<Record<string, preferences.ValueType>>,
    preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    if (typeof keys == 'string') {
      keys = [keys];
    }
    let preferences = PreferencesHelper.getPreferencesSync(preferenceName); //获取实例
    preferences.on('dataChange', keys, callback);
  }

  /**
   * 取消精确订阅数据变更。
   * @param keys 需要订阅的key集合。
   * @param callback 需要取消的回调函数,若callback不填写,表示所有的callback都需要处理;若callback填写,表示只处理该callback。
   * @param preferenceName 实例的名称。
   */
  static offDataChange(keys: string | Array<string>, callback: Callback<Record<string, preferences.ValueType>>,
    preferenceName: string = PreferencesHelper.defaultPreferenceName) {
    let preferences = PreferencesHelper.getPreferencesSync(preferenceName); //获取实例
    if (typeof keys == 'string') {
      keys = [keys];
    }
    if (callback) {
      preferences.off('dataChange', keys, callback);
    } else {
      preferences.off('dataChange', keys);
    }
  }


}

1.2.2. PreferencesPage

import { Callback } from '@kit.BasicServicesKit';
import { preferences } from '@kit.ArkData';
import { LogUtil, StrUtil, ToastHelper } from 'zzslib';
import { PreferencesHelper } from 'zzslib/src/main/ets/helper/PreferencesHelper';

/**
 * Preferences(用户首选项)工具类
 */
@Entry
@Component
struct PreferencesPage {
  private scroller: Scroller = new Scroller();
  @State pStr: string = ''
  private callback: Callback<string> = (value) => {
    LogUtil.error("onChange: " + value);
  }
  private dataCallback = (data: Record<string, preferences.ValueType>) => {
    for (const keyValue of Object.entries(data)) {
      LogUtil.error("onDataChange: " + keyValue);
    }
  }
  build() {
    Column() {
      Divider()
      Scroll(this.scroller) {
        Column() {
          Button("put()\nputSync()\nget()\ngetSync")
            .labelStyle({ maxLines: 5 })
            .type(ButtonType.Normal)
            .borderRadius(10)
            .padding({ top: 10, bottom: 10 })
            .btnStyle()
            .onClick(async () => {
              PreferencesHelper.put("id", 10000031);
              PreferencesHelper.putSync("name", "zzs");
              ToastHelper.showToast("数据缓存成功");
              let id = await PreferencesHelper.get("id", "");
              let name = PreferencesHelper.getSync("name", "");
              this.pStr = `获取缓存:\nid: ${id}\nname: ${name}`;
            })
          Button("getNumber()\ngetNumberSync()\ngetString()\ngetStringSync")
            .labelStyle({ maxLines: 5 })
            .type(ButtonType.Normal)
            .borderRadius(10)
            .padding({ top: 10, bottom: 10 })
            .btnStyle()
            .onClick(async () => {
              let id1 = await PreferencesHelper.getNumber("id");
              let id2 = PreferencesHelper.getNumberSync('id');
              let name1 = await PreferencesHelper.getString("name");
              let name2 = PreferencesHelper.getStringSync('name');
              this.pStr = `获取缓存:\nid1: ${id1}\nid2: ${id2}\nname1: ${name1}\nname2: ${name2}`;
            })
          Button("getBoolean()\ngetBooleanSync()")
            .labelStyle({ maxLines: 2 })
            .type(ButtonType.Normal)
            .borderRadius(10)
            .padding({ top: 10, bottom: 10 })
            .btnStyle()
            .onClick(async () => {
              PreferencesHelper.put("bl_nut", true);
              PreferencesHelper.putSync("bl_nuv", false);
              let bl1 = await PreferencesHelper.getBoolean("bl_nut");
              let bl2 = PreferencesHelper.getBooleanSync('bl_nuv');
              this.pStr = `缓存·bl1:${bl1}\n缓存·bl2:${bl2}`;
            })
          Button("has()\nhasSync()")
            .labelStyle({ maxLines: 2 })
            .type(ButtonType.Normal)
            .borderRadius(10)
            .padding({ top: 10, bottom: 10 })
            .btnStyle()
            .onClick(async () => {
              let bl1 = await PreferencesHelper.has("id");
              let bl2 = PreferencesHelper.hasSync('name');
              this.pStr = `has~bl1:${bl1}\nhas~bl2:${bl2}`;
            })
          Button("delete()\ndeleteSync()")
            .labelStyle({ maxLines: 2 })
            .type(ButtonType.Normal)
            .borderRadius(10)
            .padding({ top: 10, bottom: 10 })
            .btnStyle()
            .onClick(async () => {
              PreferencesHelper.delete("id");
              PreferencesHelper.deleteSync("name");
              ToastHelper.showToast('删除缓存成功')
            })
          Button("clear()\nclearSync()")
            .labelStyle({ maxLines: 2 })
            .type(ButtonType.Normal)
            .borderRadius(10)
            .padding({ top: 10, bottom: 10 })
            .btnStyle()
            .onClick(() => {
              PreferencesHelper.clear();
              PreferencesHelper.clearSync();
              ToastHelper.showToast('清空缓存成功')
            })
          Button("putSync(),其他使用\ngetStringSync(),其他使用")
            .labelStyle({ maxLines: 2 })
            .type(ButtonType.Normal)
            .borderRadius(10)
            .padding({ top: 10, bottom: 10 })
            .btnStyle()
            .onClick(() => {
              PreferencesHelper.putSync("msg",
                "Harmony_utils,一款高效的HarmonyOS工具包,封装了常用工具类,提供一系列简单易用的方法。帮助开发者快速构建鸿蒙应用。",
                "SP_PREFERENCES_MSG");
              let cacheMsg = PreferencesHelper.getStringSync("msg", "SP_PREFERENCES_MSG");
              this.pStr = `缓存:\n${cacheMsg}`;
            })
          Button("deletePreferences()")
            .btnStyle()
            .onClick(() => {
              PreferencesHelper.deletePreferences(getContext(), "SP_PREFERENCES_MSG");
              ToastHelper.showToast('移出指定的Preferences实例,成功')
            })
          Button("onChange()")
            .btnStyle()
            .onClick(() => {
              PreferencesHelper.onChange(this.callback);
            })
          Button("offChange()")
            .btnStyle()
            .onClick(() => {
              PreferencesHelper.offChange(this.callback);
            })
          Button("onDataChange()")
            .btnStyle()
            .onClick(() => {
              PreferencesHelper.onDataChange(["id", "name"], this.dataCallback);
            })
          Button("offDataChange()")
            .btnStyle()
            .onClick(() => {
              PreferencesHelper.offDataChange(["id", "name"], this.dataCallback);
            })

          Blank().layoutWeight(1)
        }
        .margin({ top: 5, bottom: 5 })
      }
      .layoutWeight(1)

      Text(this.pStr)
        .visibility(StrUtil.isNotEmpty(this.pStr) ? Visibility.Visible : Visibility.None)
        .textStyle()
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Start)
    .backgroundColor($r('app.color.primary_bg'))
  }
}


@Styles
function btnStyle() {
  .width('90%')
  .margin({ top: 10, bottom: 5 })
}

@Styles
function textStyle() {
  .width('95%')
  .padding(10)
  .shadow(ShadowStyle.OUTER_DEFAULT_XS)
  .margin({ top: 5, bottom: 10 })
  .backgroundColor(Color.White)
  .border({
    width: 1,
    color: Color.Grey,
    radius: 10,
    style: BorderStyle.Dashed
  })
}

1.7. Preferences存储json格式字符串时报错?

  Preferences存储json格式字符串时报错 ,
  无论是转成:

let uInt8Array = new util.TextEncoder().encodeInto(value);
this.preferences.putSync(“asd” , uInt8Array)

  还是直接写成:

this.preferences.putSync(“asd” ,value)

  都会报错:

Parameter error. The type of ‘value’ must be ValueType.

  原因:value值过长导致的,首选项value的最大长度限制为8192个字节。可考虑使用kv数据库或者rdb进行储存。

10-18 19:33