在 OpenHarmony 应用开发中,数据持久化有多种方式。
1. 数据持久化
1.1 用户首选项(Preferences)
-
特点:通常用于保存应用的配置信息,数据以文本形式保存在设备中,应用使用时会将文本中的数据全量加载到内存,访问速度快、效率高,但不适合存储大量数据,适用于保存用户个性化设置等。
-
使用步骤:
-
获取 Preferences 实例:通过
context
获取Preferences
实例,每个文件唯一对应一个Preferences
实例,系统会将其存储在内存中。 -
保存数据:使用
put
方法将数据保存到Preferences
实例中,例如preferences.putString("key", "value");
。 -
读取数据:使用
getString
等方法从Preferences
实例中读取数据,如String value = preferences.getString("key", "defaultValue");
。 -
持久化数据:使用
flush
接口将内存中的数据写入持久化文件,例如preferences.flush();
。
-
1.2 键值型数据库(KV - Store)
-
特点:一种非关系型数据库,数据以 “键值” 对形式组织、索引和存储,“键” 作为唯一标识符。适合很少数据关系和业务关系的业务数据存储,在分布式场景中能降低数据库版本兼容和数据同步冲突解决的复杂度,相比关系型数据库更容易跨设备跨版本兼容。
-
使用步骤:
-
创建 KV - Store 实例:通过相关配置和
context
创建KV - Store
实例。 -
保存数据:使用
put
方法保存键值对数据,如kvStore.put("key", "value");
。 -
读取数据:使用
get
方法读取数据,例如String value = kvStore.get("key");
。 -
删除数据:使用
delete
方法删除指定键的数据,kvStore.delete("key");
。
-
1.3 关系型数据库(RelationalStore)
-
特点:以行和列的形式存储数据,广泛用于处理关系型数据,提供一系列增、删、改、查接口,开发者还可运行自定义 SQL 语句满足复杂业务场景需求。
-
使用步骤:
-
获取 RdbStore:调用
getRdbStore
方法获得一个RdbStore
来操作关系型数据库, -
创建表:通过
executeSql
方法执行 SQL 语句创建表,如store.executeSql('CREATE TABLE IF NOT EXISTS mytable (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)');
。 -
插入数据:使用
insert
方法插入数据, -
查询数据:使用
query
方法查询数据,例如store.query('SELECT * FROM mytable', [], (err, resultSet) => { /* 处理查询结果 */ });
。 -
更新数据:通过
executeSql
方法执行 SQL 更新语句,如store.executeSql('UPDATE mytable SET age = ? WHERE name = ?', [35, 'John']);
。 -
删除数据:使用
executeSql
方法执行 SQL 删除语句,例如store.executeSql('DELETE FROM mytable WHERE age > ?', [40]);
-
2.用户首选项具体实现
在这里我们采用用户首选项键值型数据库。
1.1登录功能
用户首选项(Preferences)在 OpenHarmony 中用于轻量级数据持久化,通常存储应用配置、用户设置或小型数据(如登录令牌),它的核心作用是:存储登录状态并实现自动登录。通过在应用启动时初始化并检查令牌,确保用户体验的连贯性和安全性。
-
存储登录状态:通过保存和读取
TOKEN_KEY
(认证令牌),判断用户是否已登录。 -
实现自动登录:应用启动时检查令牌有效性,自动导航到主页或登录页。
在EntryAbility.ts文件中增加用户首选项。
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
import PreferencesUtil from '../Utils/Prefereces';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
PreferencesUtil.createPreferences(this.context).catch(err => {
hilog.error(0x0000, 'testTag', 'Failed to init preferences: %{public}s', err);
});
}
onDestroy(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
try {
const token = await PreferencesUtil.getToken();
const targetPage = token ? 'pages/Index' : 'pages/Login';
windowStage.loadContent(targetPage, (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading %{public}s', targetPage);
});
} catch (err) {
hilog.error(0x0000, 'testTag', 'Failed to get token. Cause: %{public}s', JSON.stringify(err) ?? '');
windowStage.loadContent('pages/Login', () => {});
}
}
onWindowStageDestroy(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}
2.2基于API9
API9的EntryAbility.ts不支持导入ets文件,仅支持js文件。
2.2.1 功能概述
用户首选项工具类封装了 OpenHarmony 的dataPreferences
API,提供了对应用偏好设置的管理功能:
-
创建偏好设置实例
-
保存和读取认证令牌(Token)
-
删除特定键值对
-
清空所有偏好数据
2.2.2 代码实现解析
1. 模块导入与常量定义
import dataPreferences from '@ohos.data.preferences';
const PREFERENCES_NAME = 'myPreferences';
const TOKEN_KEY = 'TOKEN_KEY';
-
定义偏好文件名和 Token 键名(使用常量提高可维护性)
2. 类结构与静态属性
export default class PreferencesUtil {
static preferences = null;
}
-
使用静态属性存储单例 Preferences 实例
-
设计为工具类模式(所有方法为静态方法),无需实例化
3. 初始化方法
static async createPreferences(context) {
try {
this.preferences = await dataPreferences.getPreferences(
context,
PREFERENCES_NAME
);
} catch (err) {
console.error(`Failed to create preferences. Cause: ${err}`);
throw err;
}
}
-
异步获取 Preferences 实例(需要应用上下文 context)
-
使用单例模式确保全局只有一个实例
-
错误处理采用日志记录 + 向上抛出异常的方式
4. Token 管理方法
// 保存Token(带持久化)
static async saveToken(token) {
await this.preferences.put(TOKEN_KEY, token);
await this.preferences.flush();
}
// 获取Token(带默认值)
static async getToken() {
return await this.preferences.get(TOKEN_KEY, '');
}
// 删除Token(带持久化)
static async deleteToken() {
await this.preferences.delete(TOKEN_KEY);
await this.preferences.flush();
}
-
所有操作均为异步方法
-
使用
flush()
确保数据写入磁盘 -
get()
方法提供默认值(空字符串)
5. 清空数据方法
static async clearAll() {
await this.preferences.clear();
await this.preferences.flush();
}
-
清空所有键值对(保留文件结构)
-
同样调用
flush()
确保持久化
6.完整代码
Prefereces.js文件
import dataPreferences from '@ohos.data.preferences';
const PREFERENCES_NAME = 'myPreferences'; // 首选项名字
const TOKEN_KEY = 'TOKEN_KEY'; // 首选项Key字段
export default class PreferencesUtil {
static preferences = null;
// 创建dataPreferences
static async createPreferences(context) {
try {
// 获取首选项实例
this.preferences = await dataPreferences.getPreferences(
context,
PREFERENCES_NAME
);
} catch (err) {
console.error(`Failed to create preferences. Cause: ${err}`);
throw err;
}
}
// 保存token
static async saveToken(token) {
if (!this.preferences) {
throw new Error('Preferences not initialized. Call createPreferences() first.');
}
try {
await this.preferences.put(TOKEN_KEY, token);
await this.preferences.flush();
} catch (err) {
console.error(`Failed to save token. Cause: ${err}`);
throw err;
}
}
// 获取token
static async getToken() {
if (!this.preferences) {
throw new Error('Preferences not initialized. Call createPreferences() first.');
}
try {
return await this.preferences.get(TOKEN_KEY, '');
} catch (err) {
console.error(`Failed to get token. Cause: ${err}`);
return '';
}
}
// 删除token
static async deleteToken() {
if (!this.preferences) {
throw new Error('Preferences not initialized. Call createPreferences() first.');
}
try {
await this.preferences.delete(TOKEN_KEY);
await this.preferences.flush();
} catch (err) {
console.error(`Failed to delete token. Cause: ${err}`);
throw err;
}
}
// 清空所有首选项数据
static async clearAll() {
if (!this.preferences) {
throw new Error('Preferences not initialized. Call createPreferences() first.');
}
try {
await this.preferences.clear();
await this.preferences.flush();
} catch (err) {
console.error(`Failed to clear preferences. Cause: ${err}`);
throw err;
}
}
}
2.3 基于API10及以上
API10及以上的EntryAbility.ts支持导入ets文件
流程与API9js文件无大致差异,此处仅贴出完整工具类代码:
import { preferences } from "@kit.ArkData";
export class PreferencesUtil01 {
static defaultStore: string = 'default_store1'
static Token_key: string = 'token'
// 1.创建仓库
static getStore(content: Context, storeName?: string) {
const store = preferences.getPreferencesSync(content, {
name: storeName || PreferencesUtil01.defaultStore
})
return store
}
// 2.存储Token
static async setToken(content: Context, storeName: string, value: string) {
const store = PreferencesUtil01.getStore(content, storeName)
await store.put(PreferencesUtil01.Token_key, value)
await store.flush()
}
// 3.获取Token
static getToken(content: Context, storeName: string, token_KEY?: string) {
const store = PreferencesUtil01.getStore(content, storeName)
const token = store.getSync(token_KEY || PreferencesUtil01.Token_key, '')
return token
}
// 4.删除Token
static async deleteToken(content: Context, storeName: string) {
const store = PreferencesUtil01.getStore(content, storeName)
await store.delete(PreferencesUtil01.Token_key)
await store.flush()
}
// 5.存储任意键值对
static async setValue(content: Context, storeName: string, key: string, value: string) {
const store = PreferencesUtil01.getStore(content, storeName)
await store.put(key, value)
await store.flush()
}
// 6.获取任意键值对
static getValue(content: Context, storeName: string, key: string, defaultValue: string = '') {
const store = PreferencesUtil01.getStore(content, storeName)
return store.getSync(key, defaultValue)
}
// 7.删除任意键值对
static async deleteValue(content: Context, storeName: string, key: string) {
const store = PreferencesUtil01.getStore(content, storeName)
await store.delete(key)
await store.flush()
}
// 8.清空整个仓库
static async clearStore(content: Context, storeName: string) {
const store = PreferencesUtil01.getStore(content, storeName)
await store.clear()
await store.flush()
}
}