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实例,系统会通过静态容器将该实例存储在内存中,直到主动从内存中移除该实例或者删除该文件。
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. 完整代码
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进行储存。