HarmonyOS数据管理与应用数据持久化(一)

一. 数据管理概述

功能介绍

数据管理为开发者提供数据存储、数据管理能力,比如联系人应用数据可以保存到数据库中,提供数据库的安全、可靠等管理机制。

● 数据存储:提供通用数据持久化能力,根据数据特点,分为用户首选项、键值型数据库和关系型数据库。

● 数据管理:提供高效的数据管理能力,包括权限管理、数据备份恢复、数据共享框架等。

应用创建的数据库,都保存到应用沙盒,当应用卸载时,数据库也会自动删除。

运作机制

数据管理模块包括用户首选项、键值型数据管理、关系型数据管理、分布式数据对象和跨应用数据管理。Interface接口层提供标准JS API接口,定义这些部件接口描述,供开发者参考。Frameworks&System service层负责实现部件数据存储功能,还有一些SQLite和其他子系统的依赖。

图1 数据管理架构图

● 用户首选项(Preferences):提供了轻量级配置数据的持久化能力,并支持订阅数据变化的通知能力。不支持分布式同步,常用于保存应用配置信息、用户偏好设置等。

● 键值型数据管理(KV-Store):提供了键值型数据库的读写、加密、手动备份能力。分布式功能暂不支持。

● 关系型数据管理(RelationalStore):提供了关系型数据库的增删改查、加密、手动备份能力。分布式功能暂不支持。

● 分布式数据对象(DataObject):独立提供对象型结构数据的分布式能力。分布式功能暂不支持。

● 跨应用数据管理(DataShare):提供了向其他应用共享以及管理其数据的方法。仅系统应用可用,非系统应用无需关注,下文不做具体介绍。

二. 应用数据持久化概述

应用数据持久化,是指应用将内存中的数据通过文件或数据库的形式保存到设备上。内存中的数据形态通常是任意的数据结构或数据对象,存储介质上的数据形态可能是文本、数据库、二进制文件等。

HarmonyOS标准系统支持典型的存储数据形态,包括用户首选项、键值型数据库、关系型数据库。

开发者可以根据如下功能介绍,选择合适的数据形态以满足自己应用数据的持久化需要。

● 用户首选项(Preferences):通常用于保存应用的配置信息。数据通过文本的形式保存在设备中,应用使用过程中会将文本中的数据全量加载到内存中,所以访问速度快、效率高,但不适合需要存储大量数据的场景。

● 键值型数据库(KV-Store):一种非关系型数据库,其数据以"键值"对的形式进行组织、索引和存储,其中"键"作为唯一标识符。适合很少数据关系和业务关系的业务数据存储,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。

● 关系型数据库(RelationalStore):一种关系型数据库,以行和列的形式存储数据,广泛用于应用中的关系型数据的处理,包括一系列的增、删、改、查等接口,开发者也可以运行自己定义的SQL语句来满足复杂业务场景的需要。

三. 通过用户首选项实现数据持久化

场景介绍

用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。当用户希望有一个全局唯一存储的地方,可以采用用户首选项来进行存储。Preferences会将该数据缓存在内存中,当用户读取的时候,能够快速从内存中获取数据。Preferences会随着存放的数据量越多而导致应用占用的内存越大,因此,Preferences不适合存放过多的数据,适用的场景一般为应用保存用户的个性化设置(字体大小,是否开启夜间模式)等。

运作机制

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

应用首选项的持久化文件保存在应用沙箱内部,可以通过context获取其路径。具体可见获取应用开发路径

约束限制

● Key键为string类型,要求非空且长度不超过80个字节。

● 如果Value值为string类型,可以为空,不为空时长度不超过8192个字节。

● 内存会随着存储数据量的增大而增大,所以存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销。

接口说明

以下是用户首选项持久化功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见用户首选项

接口名称 描述
getPreferences(context: Context, name: string, callback: AsyncCallback): void 获取Preferences实例。
put(key: string, value: ValueType, callback: AsyncCallback): void 将数据写入Preferences实例,可通过flush将Preferences实例持久化。
has(key: string, callback: AsyncCallback): void 检查Preferences实例是否包含名为给定Key的存储键值对。给定的Key值不能为空。
get(key: string, defValue: ValueType, callback: AsyncCallback): void 获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue。
delete(key: string, callback: AsyncCallback): void 从Preferences实例中删除名为给定Key的存储键值对。
flush(callback: AsyncCallback): void 将当前Preferences实例的数据异步存储到用户首选项持久化文件中。
on(type: 'change', callback: Callback<{ key : string }>): void 订阅数据变更,订阅的Key的值发生变更后,在执行flush方法后,触发callback回调。
off(type: 'change', callback?: Callback<{ key : string }>): void 取消订阅数据变更。
deletePreferences(context: Context, name: string, callback: AsyncCallback): void 从内存中移除指定的Preferences实例。若Preferences实例有对应的持久化文件,则同时删除其持久化文件。

开发步骤

  1. 导入用户首选项模块。
javascript 复制代码
import dataPreferences from '@ohos.data.preferences';
  1. 要通过用户首选项实现数据持久化,首先要获取Preferences实例。读取指定文件,将数据加载到Preferences实例,用于数据操作。Stage模型示例:
javascript 复制代码
import UIAbility from '@ohos.app.ability.UIAbility';

class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage) {
    try {
      dataPreferences.getPreferences(this.context, 'mystore', (err, preferences) => {
        if (err) {
          console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
          return;
        }
        console.info('Succeeded in getting preferences.');
        // 进行相关数据操作
      })
    } catch (err) {
      console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
    }
  }
}

写入数据。使用put()方法保存数据到缓存的Preferences实例中。在写入数据后,如有需要,可使用flush()方法将Preferences实例的数据存储到持久化文件。

说明

当对应的键已经存在时,put()方法会修改其值。如果仅需要在键值对不存在时新增键值对,而不修改已有键值对,需使用has()方法检查是否存在对应键值对;如果不关心是否会修改已有键值对,则直接使用put()方法即可。

示例代码如下所示:

javascript 复制代码
try {
  preferences.has('startup', function (err, val) {
    if (err) {
      console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`);
      return;
    }
    if (val) {
      console.info("The key 'startup' is contained.");
    } else {
      console.info("The key 'startup' does not contain.");
      // 此处以此键值对不存在时写入数据为例
      try {
        preferences.put('startup', 'auto', (err) => {
          if (err) {
            console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);
            return;
          }
          console.info('Succeeded in putting data.');
        })
      } catch (err) {
        console.error(`Failed to put data. Code: ${err.code},message:${err.message}`);
      }
    }
  })
} catch (err) {
  console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`);
}
  1. 读取数据。使用get()方法获取数据,即指定键对应的值。如果值为null或者非默认值类型,则返回默认数据。示例代码如下所示:
javascript 复制代码
try {
  preferences.get('startup', 'default', (err, val) => {
    if (err) {
      console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info(`Succeeded in getting value of 'startup'. val: ${val}.`);
  })
} catch (err) {
  console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`);
}
  1. 删除数据。使用delete()方法删除指定键值对,示例代码如下所示:
javascript 复制代码
try {
  preferences.delete('startup', (err) => {
    if (err) {
      console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info("Succeeded in deleting the key 'startup'.");
  })
} catch (err) {
  console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`);
}
  1. 数据持久化。应用存入数据到Preferences实例后,可以使用flush()方法实现数据持久化。示例代码如下所示:
javascript 复制代码
try {
  preferences.flush((err) => {
    if (err) {
      console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info('Succeeded in flushing.');
  })
} catch (err) {
  console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
}
  1. 订阅数据变更。应用订阅数据变更需要指定observer作为回调方法。订阅的Key值发生变更后,当执行flush()方法时,observer被触发回调。示例代码如下所示
javascript 复制代码
let observer = function (key) {
  console.info('The key' + key + 'changed.');
}
preferences.on('change', observer);
// 数据产生变更,由'auto'变为'manual'
preferences.put('startup', 'manual', (err) => {
  if (err) {
    console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`);
    return;
  }
  console.info("Succeeded in putting the value of 'startup'.");
  preferences.flush((err) => {
    if (err) {
      console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info('Succeeded in flushing.');
  })
})

删除指定文件。使用deletePreferences()方法从内存中移除指定文件对应的Preferences实例,包括内存中的数据。若该Preference存在对应的持久化文件,则同时删除该持久化文件,包括指定文件及其备份文件、损坏文件。

说明

● 调用该接口后,应用不允许再使用该Preferences实例进行数据操作,否则会出现数据一致性问题。

● 成功删除后,数据及文件将不可恢复。

示例代码如下所示:

javascript 复制代码
try {
  dataPreferences.deletePreferences(this.context, 'mystore', (err, val) => {
    if (err) {
      console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info('Succeeded in deleting preferences.');
  })
} catch (err) {
  console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
}
相关推荐
zhanshuo12 小时前
在鸿蒙里优雅地处理网络错误:从 Demo 到实战案例
harmonyos
zhanshuo12 小时前
在鸿蒙中实现深色/浅色模式切换:从原理到可运行 Demo
harmonyos
whysqwhw17 小时前
鸿蒙分布式投屏
harmonyos
whysqwhw18 小时前
鸿蒙AVSession Kit
harmonyos
whysqwhw20 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw21 小时前
鸿蒙音频编码
harmonyos
whysqwhw21 小时前
鸿蒙音频解码
harmonyos
whysqwhw21 小时前
鸿蒙视频解码
harmonyos
whysqwhw21 小时前
鸿蒙视频编码
harmonyos
ajassi200021 小时前
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器
华为·开源·harmonyos