HarmonyOS Developer 开发指导
首选项概述
首选项Preferences,适用于对Key-Value结构的数据进行存取和持久化操作。
应用获取某个Preferences对象后,该存储对象中的数据将会被缓存在内存中,以便应用获得更快的数据存取速度。
应用也可以将缓存的数据再次写回文本文件中进行持久化存储,由于文件读写将产生不可避免的系统资源开销,建议应用降低对持久化文件的读写频率。
关于数据库锁机制,开发者无需关注其具体实现。
基本概念
- Key-Value数据结构
一种键值型的数据结构。Key是不重复的关键字,Value是数据值。 - 非关系型数据库
区别于关系数据库,不保证遵循ACID(Atomic、Consistency、Isolation及Durability)特性,不采用关系模型来组织数据,数据之间无关系。比如,以Key-Value数据结构组成的数据库。
运作机制
- 应用通过指定首选项持久化文件将其中的数据加载到Preferences实例,系统会通过静态容器将该实例存储在内存中,同一应用或进程中每个文件仅存在一个Preferences实例,直到应用主动从内存中移除该实例或者删除该首选项持久化文件。
- 应用获取到首选项持久化文件对应的实例后,可以从Preferences实例中读取数据,或者将数据存入Preferences实例中。通过调用flush方法可以将Preferences实例中的数据回写到文件里。
图1 首选项运作机制
约束与限制
- 因Preferences实例会加载到内存中,建议存储的数据不超过一万条,并注意及时清理不再使用的实例,以便减少非内存开销。
- 数据中的Key为string类型,要求非空且字符长度不超过80个字节。
- 当数据中的Value为string类型时,允许为空,字符长度不超过8192个字节。
首选项开发指导
说明
该功能特性从API Version 9开始支持。API Version 9之前可使用轻量级存储的相关功能接口。
场景介绍
首选项功能通常用于保存应用的一些常用配置信息,并不适合需要存储大量数据和频繁改变数据的场景。应用的数据保存在文件中,这些文件可以持久化地存储在设备上。
需要注意的是,应用访问的实例包含文件所有数据,这些数据会一直加载在设备的内存中,直到应用主动从内存中将其移除前,应用都可以通过Preferences API进行相关数据操作。
接口说明
首选项为应用提供Key-Value键值型的文件数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。
数据存储形式为键值对,键的类型为字符串型,值的存储数据类型包括数字型、字符型、布尔型以及这3种类型的数组类型。
更多首选项相关接口,请见首选项API。
创建存储实例
读取指定文件,将数据加载到Preferences实例,即可创建一个存储实例,用于数据操作。
表1 首选项实例创建接口
包名 | 接口名 | 描述 |
ohos.data.preferences | getPreferences(context: Context, name: string): Promise<Preferences> | 读取指定首选项持久化文件,将数据加载到Preferences实例,用于数据操作。 |
数据处理
通过put系列方法,可以增加或修改Preferences实例中的数据。
通过调用get系列方法,可以读取Preferences中的数据。
通过调用getAll系列方法,可以获取Preferences中包含所有键值的Object对象。
通过调用delete系列方法,可以删除Preferences中名为给定Key的存储键值对。
表2 首选项数据处理接口
类名 | 接口名 | 描述 |
Preferences | put(key: string, value: ValueType): Promise<void> | 支持存入值为number、string、boolean、Array<number>、Array<string>、Array<boolean>类型的数据。 |
Preferences | get(key: string, defValue: ValueType): Promise<ValueType> | 支持获取值为number、string、boolean、Array<number>、Array<string>、Array<boolean>类型的数据。 |
Preferences | getAll(): Promise<Object> | 支持获取含有所有键值的Object对象。 |
Preferences | delete(key: string): Promise<void> | 支持从Preferences实例中删除名为给定Key的存储键值对。 |
数据持久化
通过执行flush方法,应用可以将缓存的数据再次写回文本文件中进行持久化存储。
表4 首选项持久化接口
类名 | 接口名 | 描述 |
Preferences | flush(): Promise<void> | 将Preferences实例通过异步线程回写入文件中。 |
订阅数据变更
订阅数据变更,订阅的Key的值发生变更后,在执行flush方法后,会触发callback回调。
表5 首选项变化订阅接口
类名 | 接口名 | 描述 |
Preferences | on(type: 'change', callback: Callback<{ key : string }>): void | 订阅数据变更。 |
Preferences | off(type: 'change', callback: Callback<{ key : string }>): void | 注销订阅。 |
删除数据文件
通过调用以下两种接口,可以删除数据实例或对应的文件。
表6 首选项删除接口
包名 | 接口名 | 描述 |
ohos.data.preferences | deletePreferences(context: Context, name: string): Promise<void> | 从缓存中移除已加载的Preferences对象,同时从设备上删除对应的文件。 |
ohos.data.preferences | removePreferencesFromCache(context: Context, name: string): Promise<void> | 仅从缓存中移除已加载的Preferences对象,主要用于释放内存。 |
开发步骤
- 准备工作,导入@ohos.data.preferences以及相关的模块到开发环境。
import data_preferences from '@ohos.data.preferences';
- 获取Preferences实例。
读取指定文件,将数据加载到Preferences实例,用于数据操作。
FA模型示例:
// 获取context
import featureAbility from '@ohos.ability.featureAbility'
let context = featureAbility.getContext();
let preferences = null;
let promise = data_preferences.getPreferences(context, 'mystore');
promise.then((pref) => {
preferences = pref;
}).catch((err) => {
console.info("Failed to get preferences.");
})
Stage模型示例:
import UIAbility from '@ohos.app.ability.UIAbility';
let preferences = null;
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
let promise = data_preferences.getPreferences(this.context, 'mystore');
promise.then((pref) => {
preferences = pref;
}).catch((err) => {
console.info("Failed to get preferences.");
})
}
}
- 存入数据。
使用put方法保存数据到缓存的实例中。
let putPromise = preferences.put('startup', 'auto');
putPromise.then(() => {
console.info("Succeeded in putting the value of 'startup'.");
}).catch((err) => {
console.info("Failed to put the value of 'startup'. Cause: " + err);
})
- 读取数据。
使用get方法读取数据。
let getPromise = preferences.get('startup', 'default');
getPromise.then((value) => {
console.info("The value of 'startup' is " + value);
}).catch((err) => {
console.info("Failed to get the value of 'startup'. Cause: " + err);
})
- 数据持久化。
应用存入数据到Preferences实例后,可以通过flush方法将Preferences实例回写到文件中。
preferences.flush();
- 订阅数据变更。
应用订阅数据变更需要指定observer作为回调方法。订阅的Key的值发生变更后,当执行flush方法时,observer被触发回调。
let observer = function (key) {
console.info("The key" + key + " changed.");
}
preferences.on('change', observer);
// 数据产生变更,由'auto'变为'manual'
preferences.put('startup', 'manual', function (err) {
if (err) {
console.info("Failed to put the value of 'startup'. Cause: " + err);
return;
}
console.info("Succeeded in putting the value of 'startup'.");
preferences.flush(function (err) {
if (err) {
console.info("Failed to flush. Cause: " + err);
return;
}
console.info("Succeeded in flushing."); // observer will be called.
})
})
- 删除指定文件。
使用deletePreferences方法从内存中移除指定文件对应的Preferences单实例,并删除指定文件及其备份文件、损坏文件。删除指定文件时,应用不允许再使用该实例进行数据操作,否则会出现数据一致性问题。删除后,数据及文件将不可恢复。
let proDelete = data_preferences.deletePreferences(context, 'mystore');
proDelete.then(() => {
console.info("Succeeded in deleting.");
}).catch((err) => {
console.info("Failed to delete. Cause: " + err);
})
分布式数据对象概述
分布式数据对象管理框架是一款面向对象的内存数据管理框架。向应用开发者提供内存对象的创建、查询、删除、修改、订阅等基本数据对象的管理能力;同时具备分布式能力,满足超级终端场景下,相同应用多设备间的数据对象协同需求。
基本概念
- 分布式内存数据库
分布式内存数据库将数据缓存在内存中,以便应用获得更快的数据存取速度,不会将数据进行持久化。若数据库关闭,则数据不会保留。 - 分布式数据对象
分布式数据对象是一个JS对象型的封装。每一个分布式数据对象实例会创建一个内存数据库中的数据表,每个应用程序创建的内存数据库相互隔离,对分布式数据对象的“读取”或“赋值”会自动映射到对应数据库的get/put操作。
分布式数据对象的生命周期包括以下状态:
○ 未初始化:未实例化,或已被销毁。
○ 本地数据对象:已创建对应的数据表,但是还无法进行数据同步。
○ 分布式数据对象:已创建对应的数据表,设备在线且组网内设置同样sessionId的对象数>=2,可以跨设备同步数据。若设备掉线或将sessionId置为空,分布式数据对象退化为本地数据对象。
运作机制
分布式数据对象生长在分布式内存数据库之上,在分布式内存数据库上进行了JS对象型的封装,能像操作本地变量一样操作分布式数据对象,数据的跨设备同步由系统自动完成。
图1 分布式数据对象运行机制
约束与限制
- 不同设备间只有相同bundleName的应用才能直接同步。
- 不建议创建过多的分布式数据对象,每个分布式数据对象将占用100-150KB内存。
- 每个分布式数据对象大小不超过500KB。
- 如对复杂类型的数据进行修改,仅支持修改根属性,暂不支持下级属性修改。
- 支持JS接口间的互通,与其他语言不互通。
分布式数据对象开发指导
场景介绍
分布式数据对象为开发者在分布式应用场景下提供简单易用的功能接口,可实现多设备间同应用的数据协同,同时设备间还可以监听对象的状态和数据变更。
比如,当设备1上应用A的分布式数据对象增、删、改数据后,设备2上应用A也可以获取到对应的数据变化,同时还能监听数据变更以及对端数据对象的上下线。
接口说明
分布式数据对象相关功能接口请见分布式数据对象。
创建数据对象实例
创建一个分布式数据对象实例,开发者可以通过source指定分布式数据对象中的属性。
表1 分布式数据对象实例创建接口
包名 | 接口名 | 描述 |
ohos.data.distributedDataObject | createDistributedObject(source: object): DistributedObject | 创建一个分布式数据对象实例,用于数据操作。 - source:设置分布式数据对象的属性。 - DistributedObject:返回值是创建好的分布式数据对象。 |
创建分布式数据对象sessionId
创建一个随机的sessionId,可将其设置为一个分布式数据对象的sessionId。
表2 分布式数据对象sessionId创建接口
包名 | 接口名 | 描述 |
ohos.data.distributedDataObject | genSessionId(): string | 创建一个sessionId,可作为分布式数据对象的sessionId。 |
设置分布式数据对象sessionId
设置分布式数据对象的sessionId,sessionId是一次(多设备)协同的唯一标识,同步的多个数据对象需要关联同一个sessionId。
表3 分布式数据对象sessionId设置接口
类名 | 接口名 | 描述 |
DistributedDataObject | setSessionId(sessionId?: string): boolean | 为分布式数据对象设置sessionId。 sessionId:分布式数据对象在可信组网中的标识ID。如果要退出分布式组网,设置为""或不设置均可。 |
订阅数据变更
订阅数据变更需要指定Callback作为回调方法,订阅的数据对象发生数据变更后,Callback被回调。
表4 分布式数据对象数据变更订阅接口
类名 | 接口名 | 描述 |
DistributedDataObject | on(type: 'change', callback: Callback<{ sessionId: string, fields: Array<string> }>): void | 订阅数据变更。 |
DistributedDataObject | off(type: 'change', callback?: Callback<{ sessionId: string, fields: Array<string> }>): void | 注销订阅。需要删除的变更回调,若不设置则删除该对象所有的变更回调。 |
订阅数据对象上下线
订阅数据对象上下线需要指定Callback作为回调方法,订阅的数据对象上线/下线后,对端的数据对象会收到Callback回调。
表5 分布式数据对象数据上下线订阅接口
类名 | 接口名 | 描述 |
DistributedDataObject | on(type: 'status', callback: Callback<{ sessionId: string, networkId: string, status: 'online' | 'offline' }>): void | 订阅数据对象上下线。 |
DistributedDataObject | off(type: 'status', callback?: Callback<{ sessionId: string, deviceId: string, status: 'online' | 'offline' }>): void | 注销订阅。 |
开发步骤
以一次分布式数据对象同步为例,说明开发步骤。
- 准备工作,导入@ohos.data.distributedDataObject模块到开发环境。
import distributedObject from '@ohos.data.distributedDataObject';
- 请求权限(同步操作时进行该步骤)。
需要在config.json文件里进行配置请求权限(FA模型),示例代码如下:
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
}
Stage模型下的权限请求请参见权限声明-Stage模型。
这个权限还需要在应用首次启动的时候弹窗获取用户授权,可以通过如下代码实现:
// FA模型
import featureAbility from '@ohos.ability.featureAbility';
function grantPermission() {
console.info('grantPermission');
let context = featureAbility.getContext();
context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666).then((data) => {
console.info('success: ${data}');
}).catch((error) => {
console.error('failed: ${error}');
})
}
grantPermission();
// Stage模型
import UIAbility from '@ohos.app.ability.UIAbility';
let context = null;
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
let context = this.context;
}
}
function grantPermission() {
let permissions = ['ohos.permission.DISTRIBUTED_DATASYNC'];
context.requestPermissionsFromUser(permissions).then((data) => {
console.info('success: ${data}');
}).catch((error) => {
console.log('failed: ${error}');
});
}
grantPermission();
- 获取分布式数据对象实例。
let localObject = distributedObject.createDistributedObject({
name: undefined,
age: undefined,
isVis: true,
parent: undefined,
list: undefined
});
let sessionId = distributedObject.genSessionId();
- 加入同步组网。同步组网中的数据对象分为发起方和被拉起方。
// 发起方
let localObject = distributedObject.createDistributedObject({
name: "jack",
age: 18,
isVis: true,
parent: { mother: "jack mom", father: "jack Dad" },
list: [{ mother: "jack mom" }, { father: "jack Dad" }]
});
localObject.setSessionId(sessionId);
// 被拉起方
let remoteObject = distributedObject.createDistributedObject({
name: undefined,
age: undefined,
isVis: true,
parent: undefined,
list: undefined
});
// 收到status上线后remoteObject同步数据,即name变成jack,age是18
remoteObject.setSessionId(sessionId);
- 监听对象数据变更。可监听对端数据的变更,以Callback作为变更回调实例。
function changeCallback(sessionId, changeData) {
console.info("change" + sessionId);
if (changeData != null && changeData != undefined) {
changeData.forEach(element => {
console.info("changed !" + element + " " + localObject[element]);
});
}
}
// 发起方要在changeCallback里刷新界面,则需要将正确的this绑定给changeCallback
localObject.on("change", this.changeCallback.bind(this));
- 修改对象属性,对象属性支持基本类型(数字类型、布尔类型、字符串类型)以及复杂类型(数组、基本类型嵌套等)。
localObject.name = "jack";
localObject.age = 19;
localObject.isVis = false;
localObject.parent = { mother: "jack mom", father: "jack Dad" };
localObject.list = [{ mother: "jack mom" }, { father: "jack Dad" }];
说明
针对复杂类型的数据修改,目前支持对根属性的修改,暂不支持对下级属性的修改。
// 支持的修改方式
localObject.parent = { mother: "mom", father: "dad" };
// 不支持的修改方式
localObject.parent.mother = "mom";
- 访问对象。可以通过直接获取的方式访问到分布式数据对象的属性,且该数据为组网内的最新数据。
console.info("name " + localObject["name"]);
- 删除监听数据变更。可以指定删除监听的数据变更回调;也可以不指定,这将会删除该分布式数据对象的所有数据变更回调。
// 删除变更回调changeCallback
localObject.off("change", changeCallback);
// 删除所有的变更回调
localObject.off("change");
- 监听分布式数据对象的上下线。可以监听对端分布式数据对象的上下线。
function statusCallback(sessionId, networkId, status) {
this.response += "status changed " + sessionId + " " + status + " " + networkId;
}
localObject.on("status", this.statusCallback);
- 删除监听分布式数据对象的上下线。可以指定删除监听的上下线回调;也可以不指定,这将会删除该分布式数据对象的所有上下线回调。
// 删除上下线回调statusCallback
localObject.off("status", this.statusCallback);
// 删除所有的上下线回调
localObject.off("status");
- 退出同步组网。分布式数据对象退出组网后,本地的数据变更对端不会同步。
localObject.setSessionId("");