[鸿蒙开发] HarmonyOS应用中如何实现数据持久化存储

本文详细介绍了HarmonyOS中的数据存储方案,包括ArkData提供的用户首选项(轻量级配置)、键值型数据库(KV-Store)和关系型数据库(RelationalStore),以及它们在数据持久化、同步和管理方面的特性和使用示例。
摘要由CSDN通过智能技术生成

1. 介绍

数据存储属于ArkData(方舟数据管理)的一部分。ArkData除了提供数据存储的能力,也提供了数据管理和数据同步能力,比如联系人应用数据可以保存到数据库中,提供数据库的安全、可靠以及共享访问等管理机制,也支持与手表同步联系人信息。

我们这里先了解下数据存储,应用创建的数据库,存储的数据都是会保存到应用沙盒中,当应用卸载时,数据库也会自动删除。

在ArkData中,根据数据特点,数据持久化存储的方式有以下三种:

  • 用户首选项(Preferences)

    • 类似iOS的UserDefaults或者Android的SharedPreference,提供了轻量级配置数据的持久能力;
    • 支持订阅数据变化的通知能力;
    • 不支持分布式同步,常用于保存应用配置信息、用户偏好设置等;
    • 会将文本中的数据全量加载到内存中,访问快,效率高,但不适合存储大量数据;
  • 键值型数据库(KV-Store):

    • 提供了键值型数据库的读写、加密、手动备份以及订阅通知的能力;
    • 应用需要使用键值型数据库的分布式能力时,KV-Store会将同步请求发送给DatamgrService由其完成跨设备数据同步;
  • 关系型数据库(RelationStore):

    • 提供了关系型数据库的读写、加密、手动备份以及订阅通知的能力;
    • 如果需要使用关系型数据库的分布式能力时,RelationalStore部件会将同步请求发送给DatamgrService由其完成跨设备数据同步
    • 比如如果要在App内部存储复杂的数据结构,可以使用关系型数据库;

2.用户首选项

2.1 场景介绍

用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。其会将数据缓存放在内存中,当用户读取的时候,能够从内存中快速获取数据,当需要持久化的时候可以使用flush接口将内存中的数据写入持久化文件中。

为了提升数据读写效率,用户首选项会将所有数据存到内存,随着数据量越多,会导致应用占用的内存越大,因此不适合存放过多的数据,适用的场景一般为应用保存用户的个性化设置等。

2.2 运作机制

在这里插入图片描述

从下往上看,用户首选项也是以文件的形式存储到应用沙箱内的,开发者可以将用户首选项持久化文件的内容加载到Preferences实例(一个文件对应一个Preferences实例),系统会通过静态容器将该实例存储在内存中,直到主动从内存中移除该实例或者删除该文件。

对于Key和Value有一些限制:

  • Key为string类型,非空且长度<=80个字节;
  • Value为string时,长度<=8192个字节;
  • 建议存储的数据不超过1万条;

2.3 接口说明

用户首选项和iOS的UserDefault,Android的SharedPreference的使用方法很像,都有基本的增、删、改、查方法,但不同的是用户首选项提供了订阅数据变更的能力:

// 获取Preferences实例。该接口存在异步接口。
getPreferencesSync(context: Context, options: Options): Preferences

// 将数据写入Preferences实例,可通过flush将Preferences实例持久化。该接口存在异步接口。
putSync(key: string, value: ValueType): void

// 检查Preferences实例是否包含名为给定Key的存储键值对。给定的Key值不能为空。该接口存在异步接口。
hasSync(key: string): void

// 获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue。该接口存在异步接口。
getSync(key: string, defValue: ValueType): void

// 从Preferences实例中删除名为给定Key的存储键值对。该接口存在异步接口。
deleteSync(key: string): void

// 将当前Preferences实例的数据异步存储到用户首选项持久化文件中。
flush(callback: AsyncCallback<void>): void

// 订阅数据变更,订阅的数据发生变更后,在执行flush方法后,触发callback回调。
on(type: 'change', callback: Callback<string>): void

// 取消订阅数据变更。
off(type: 'change', callback?: Callback<string>): void

// 从内存中移除指定的Preferences实例。若Preferences实例有对应的持久化文件,则同时删除其持久化文件。
deletePreferences(context: Context, options: Options, callback: AsyncCallback<void>): void

2.4 举例说明

// 导入包
import dataPreferences from '@ohos.data.preferences';

// 获取context
let context = getContext(this);
export default class PreferencesManager {

  static shared = new PreferencesManager();
  preferences?: dataPreferences.Preferences;
  preferencesName: string = 'CommonPreferences';
  
  // 初始化preferences实例
  initPreferences() {
    this.preferences = dataPreferences.getPreferencesSync(context, { name: this.preferencesName });
  }
  
  // 设置数据
  set(key: string, value: dataPreferences.ValueType) {
    if (!this.preferences) {
      this.initPreferences();
    }
    this.preferences?.putSync(key, value);
    this.preferences?.flush();
  }

  // 获取数据
  get(key: string): dataPreferences.ValueType | null | undefined {
    if (!this.preferences) {
      this.initPreferences();
    }
    let value = this.preferences?.getSync(key, null);;
    return value;
  }

  // 删除数据
  delete(key: string) {
    if (!this.preferences) {
      this.initPreferences();
    }
    if (this.preferences?.hasSync(key)) {
      this.preferences.deleteSync(key);
      this.preferences.flush();
    }
  }
}

3. 键值型数据库

3.1 介绍

键值型数据库用于存储键值对形式的数据,适用于简单的存储场景:比如存储商品名称及对应价格、员工工号及今日是否已出勤等。

  • 针对于单版本数据库,针对每条记录,Key的长度<=1KB,Value的长度<4MB;
  • 设备协同数据库,针对每条记录,Key的长度<=896 Byte, Value的长度<4MB;
  • 每个应用程序最多支持同时打开16个键值型分布式数据库;
  • 键值型数据库事件回调方法中不允许进行阻塞操作,例如修改UI组件;

3.2 接口说明

键值型数据库相关的接口,大部分为异步接口,均有callback和Promise两种返回形式。

// 创建一个KVManager对象实例,用于管理数据库对象
createKVManager(config: KVManagerConfig): KVManager

// 指定Options和storeId,创建并得到指定类型的KVStore数据库
getKVStore<T>(storeId: string, options: Options, callback: AsyncCallback<T>): void

// 添加指定类型的键值对到数据库
put(key: string, value: Uint8Array|string|number|boolean, callback: AsyncCallback<void>): void

// 获取指定键的值
get(key: string, callback: AsyncCallback<Uint8Array|string|boolean|number>): void

// 从数据库中删除指定键值的数据
delete(key: string, callback: AsyncCallback<void>): void

3.3 举例说明

// 导入模块
import distributedKVStore from '@ohos.data.distributedKVStore';

// 获取context
let context = getContext(this);
export default class KVStoreManager {
  
  // 单例模式
  static shared = new KVStoreManager();
  
  // SingleKVStore实例
  KVStore?: distributedKVStore.SingleKVStore;

  // 创建并获取KVStore
  async initKVStore() {
    try {
      // Create KVManager
      let bundleName = await getBundleName();
      const kvManagerConfig: distributedKVStore.KVManagerConfig = {
        context: context,
        bundleName: bundleName
      };
      const manager = distributedKVStore.createKVManager(kvManagerConfig);

      // Create KVStore
      const options: distributedKVStore.Options = {
        createIfMissing: true,
        encrypt: false,
        backup: false,
        autoSync: false,
        kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
        securityLevel: distributedKVStore.SecurityLevel.S1
      };
      this.KVStore = await manager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options);
    } catch (error) {
      console.log('[KVStoreManager]', `Failed to initKVStore, Cause: ${error}`);
    }
  }
  
  // 插入数据
  async set(key: string, value: Uint8Array | string | number | boolean) {
    if (!this.KVStore) {
      await this.initKVStore();
    }
    try {
      await this.KVStore?.put(key, value);
    } catch (error) {
      console.log('[KVStoreManager]', `Failed to set value, Cause: ${error}`);
    }
  }
 
  // 获取数据
  async get(key: string): Promise<string | number | boolean | Uint8Array | null | undefined> {
    if (!this.KVStore) {
      await this.initKVStore();
    }
    let value: Uint8Array | string | number | boolean | null | undefined = null;
    try {
      return this.KVStore?.get(key);
    } catch (error) {
      console.log('[KVStoreManager]', `Failed to get value, Cause: ${error}`);
    }
    return value;
  }
  
  // 删除数据
  async delete(key: string) {
    if (!this.KVStore) {
      await this.initKVStore();
    }
    try {
      await this.KVStore?.delete(key);
    } catch (error) {
      console.log('[KVStoreManager]', `Failed to delete value, Cause: ${error}`);
    }
  }
}

4. 关系型数据库

4.1 介绍

关系型数据库基于SQLite组件,适用于包含复杂关系数据的场景。比如购物车的商品数据,需要包括商品名称、价格、库存等。

基本概念:

  • 谓词:用来定义数据库的操作条件;
  • 结果集:执行查询之后的结果集合;

运作机制: 关系型数据库对应用提供通用的操作接口,底层使用SQLite作为持久化存储引擎,支持SQLite具有的数据库特性。

在这里插入图片描述

  • 数据库同一时间只能支持一个写操作;
  • ArkTS侧支持的基本数据类型:number、string、二进制类型数据、boolean;
  • 为保证插入并读取数据成功,一条数据不要超过2M;

4.2 接口说明

// 获得一个相关的RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口可以执行相关的数据操作
getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback<RdbStore>): void     

// 执行包含指定参数但不返回值的SQL语句
executeSql(sql: string, bindArgs: Array<ValueType>, callback: AsyncCallback<void>):void 

// 向目标表中插入一行数据
insert(table: string, values: ValuesBucket, callback: AsyncCallback<number>):void       

// 根据RdbPredicates的指定实例对象更新数据库中的数据
update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback<number>):void                                                |

// 根据RdbPredicates的指定实例对象从数据库中删除数据
delete(predicates: RdbPredicates, callback: AsyncCallback<number>):void                 

// 根据指定条件查询数据库中的数据
query(predicates: RdbPredicates, columns: Array<string>, callback: AsyncCallback<ResultSet>):void 

// 删除数据库
deleteRdbStore(context: Context, name: string, callback: AsyncCallback<void>): void   

4.3 举例说明

// 导入模块
import relationalStore from '@ohos.data.relationalStore';

// 获取context
let context = getContext(this);
export default class RDBStoreManager {

  private rdbStore: relationalStore.RdbStore | null = null;
  private tableName: string;
  private sqlCreateTable: string;
  private databaseName: string = '__Database.db';

  constructor(tableName: string, sqlCreateTable: string) {
    this.tableName = tableName;
    this.sqlCreateTable = sqlCreateTable;
  }
  
  // 创建rdbStore
  async getRdbStore() {
    if (!this.rdbStore) {
      console.log('RDBStoreManager', 'The rdbStore exists.');
      return
    }
    try {
      let rdb = await relationalStore.getRdbStore(context, {
        name: this.databaseName, // 数据库文件名
        securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别
        encrypt: false // 可选参数,是否加密,默认不加密
      });
      this.rdbStore = rdb;
      this.rdbStore.executeSql(this.sqlCreateTable);
    } catch (error) {
      console.log('RDBStoreManager', `gerRdbStore() failed, err: ${error}`);
    }
  }
  
  // 插入数据
  async insertData(data: relationalStore.ValuesBucket): Promise<number | null> {
    const valueBucket: relationalStore.ValuesBucket = data;
    if (this.rdbStore) {
      try {
        let result = await this.rdbStore.insert(this.tableName, valueBucket);
        return result;
      } catch (error) {
        console.log('RDBStoreManager', `insertData() failed, err: ${JSON.stringify(error)}`);
      }
    }
    return null;
  }
  
  // 删除数据
  async deleteData(predicates: relationalStore.RdbPredicates): Promise<number | null> {
    if (this.rdbStore) {
      try {
        let result = await this.rdbStore.delete(predicates);
        return result;
      } catch (error) {
        console.log('RDBStoreManager', `deleteData() failed, err: ${error}`);
      }
    }
    return null;
  }
  
  // 更新数据
  async updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket): Promise<number | null> {
    if (this.rdbStore) {
      try {
        let result = await this.rdbStore.update(data, predicates);
        return result;
      } catch (error) {
        console.log('RDBStoreManager', `updateData() failed, err: ${error}`);
      }
    }
    return null;
  }
  
  // 查询数据
  async query(predicates: relationalStore.RdbPredicates): Promise<relationalStore.ResultSet | null> {
    if (this.rdbStore) {
      try {
        let result = await this.rdbStore.query(predicates)
        return result;
      } catch (error) {
        console.log('RDBStoreManager', `query() failed, err: ${error}`);
      }
    }
    return null;
  }
}

在以上3个举例说明中,是自己根据业务需求封装的工具类,在实际使用过程中还需要进行一些更改,仅作参考。

最后

鸿蒙开发正当时,现在入手正是好时机。

还在犹豫不决的朋友们,小编在这里建议大家早点入手!

在这里分享一份鸿蒙学习路线图帮助那些不知道怎么入门的朋友,另外一些鸿蒙开发的资料文档也顺便分享给大家,扫下方二维码就能免费送呢!

鸿蒙学习路线图

路线图适合人群:

  • IT开发人员:想要拓展职业边界,享受新技术带来的溢价红利
  • 零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能
HarmonyOS 4.0 APP Developer

在这里插入图片描述

OpenHarmony 5.0 Next视音频

在这里插入图片描述

OpenHarmony 5.0 Next进阶

在这里插入图片描述

OpenHarmony 5.0 Next分布式

在这里插入图片描述

学习资料

内容概要《鸿蒙零基础入门学习指南》、《鸿蒙开发学习之UI》、《鸿蒙开发学习之Web》、《鸿蒙开发学习之应用模型》

内容特点:条理清晰,含图像化表示更加易懂。

领取方式:扫描下方二维码即可免费领取!!!

《鸿蒙零基础入门学习指南》

一、快速入门

适用于HarmonyOS应用开发的初学者。通过构建一个简单的具有页面跳转/返回功能的应用,快速了解工程目录的主要文件,熟悉HarmonyOS应用开发流程。

  • 开发准备
  • 构建第一个ArkTS应用(Stage模型)
  • 构建第一个ArkTS应用(FA模型)
  • 构建第一个JS应用(FA模型)

图片

二、开发基础知识

  • 应用程序包基础知识
  • 应用配置文件(Stage模型)
  • 应用配置文件概述(FA模型)

图片

三、资源分类与访问

应用开发过程中,经常需要用到颜色、字体、间距、图片等资源,在不同的设备或配置中,这些资源的值可能不同。

  • 资源分类与访问
  • 创建资源目录和资源文件
  • 资源访问

图片

四、学习ArkTs语言

ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集。因此,在学习ArkTS语言之前,建议开发者具备TS语言开发能力。

  • 初识ArkTS语言
  • 基本语法
  • 状态管理
  • 其他状态管理
  • 渲染控制

图片

领取方式:扫描下方二维码即可免费领取!!!

《鸿蒙开发学习之UI》

一、方舟开发框架(ArkUI)概述

图片

二、基于ArkTS声明式开发范式

  • 1、UI开发(ArkTS声明式开发范式)概述
  • 2、开发布局
  • 3、添加组件
  • 4、显示图片
  • 5、使用动画
  • 6、支持交互事件
  • 7、性能提升的推荐方法

图片

三、兼容JS的类Web开发范式

  • 1、概述
  • 2、框架说明
  • 3、构建用户界面
  • 4、常见组件开发指导
  • 5、动效开发指导
  • 6、自定义组件

图片

领取方式:扫描下方二维码即可免费领取!!!

《鸿蒙开发学习之Web》

Web组件概述

使用Web组件加载页面

图片

设置基本属性和事件

  • 设置深色模式
  • 上传文件
  • 在新窗口中打开页面
  • 管理位置权限

图片

在应用中使用前端页面JavaScript

  • 应用侧调用前端页面函数
  • 前端页面调用应用侧函数
  • 建立应用侧与前端页面数据通道
  • 管理页面跳转及浏览记录导航
  • 管理Cookie及数据存储
  • 自定义页面请求响应
  • 使用Devtools工具调试前端页面

图片

ArkTS语言基础类库概述

并发

  • 并发概述
  • 使用异步并发能力进行开发
  • 使用多线程并发能力
  • 容器类库
  • XML生成、解析、与转换
  • 通知

图片

窗口管理

  • 窗口开发概述
  • 管理应用窗口(Stage模型)
  • 管理应用窗口(FA模型)

WebGL

  • 概述
  • WebGL开发指导

媒体

  • 媒体应用开发概念
  • 图片

安全

  • 访问控制
  • ohos.permission.USE_BLUETOOTH
  • ohos.permission.DISCOVER_BLUETOOTH
  • ohos.permission.MANAGE_BLUETOOTH
  • ohos.permission.INTERNET
  • ohos.permission.INTERNET

图片

网络与连接

  • 网络管理
  • IPC与RPC通信

电话服务

  • 电话服务开发概述
  • 跳转拨号页面
  • 获取当前蜂窝网络信号信息

数据管理

  • 数据管理概述
  • 应用数据持久化
  • 场景介绍
  • 运作机制
  • 约束限制
  • 接口说明

图片

文件管理

  • 文件管理概述
  • 应用文件
  • 用户文件
  • 分布式文件系统

后台任务管理

  • 后台任务总体概述
  • 短时任务
  • 长时任务
  • 延时任务
  • 代理提醒

设备管理

  • USB服务
  • 位置服务
  • 传感器
  • 分布式跟踪开发指导
  • 错误管理

在这里插入图片描述

国际化

  • 国际化开发概述
  • Intl开发指导
  • I18n开发指导

应用测试

  • 自动化测试框架使用指南

Native API相关指导

  • Native API在应用工程中的使用指导
  • Drawing开发指导
  • Rawfile开发指导
  • NativeWindow 开发指导

图片

领取方式:扫描下方二维码即可免费领取!!!

《鸿蒙开发学习之应用模型》

应用模型概述

  • 应用模型的构成要素
  • 应用模型解读

图片

Stage模型开发指导

  • Stage模型开发概述
  • Stage模型应用组件

图片

FA模型开发指导

  • FA模型开发概述
  • FA模型应用组件配置
  • 进程模型
  • 线程模型

图片

领取方式:扫描下方二维码即可免费领取!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值