ArkUI-应用数据持久化

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

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

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

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

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

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

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

约束限制

  • 首选项无法保证进程并发安全,会有文件损坏和数据丢失的风险,不支持在多进程场景下使用。

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

  • 如果Value值为string类型,请使用UTF-8编码格式,可以为空,不为空时长度不超过16 * 1024 * 1024个字节。

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

  • 接口说明

  • 以下是用户首选项持久化功能的相关接口,更多接口及使用方式请见用户首选项。

  • 接口名称描述
    getPreferencesSync(context: Context, options: Options): Preferences获取Preferences实例。该接口存在异步接口。
    putSync(key: string, value: ValueType): void将数据写入Preferences实例,可通过flush将Preferences实例持久化。该接口存在异步接口。
    hasSync(key: string): boolean检查Preferences实例是否包含名为给定Key的存储键值对。给定的Key值不能为空。该接口存在异步接口。
    getSync(key: string, defValue: ValueType): ValueType获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue。该接口存在异步接口。
    deleteSync(key: string): void从Preferences实例中删除名为给定Key的存储键值对。该接口存在异步接口。
    flush(callback: AsyncCallback<void>): void将当前Preferences实例的数据异步存储到用户首选项持久化文件中。
    on(type: 'change', callback: Callback<string>): void订阅数据变更,订阅的数据发生变更后,在执行flush方法后,触发callback回调。
    off(type: 'change', callback?: Callback<string>): void取消订阅数据变更。
    deletePreferences(context: Context, options: Options, callback: AsyncCallback<void>): void从内存中移除指定的Preferences实例。若Preferences实例有对应的持久化文件,则同时删除其持久化文件。
  • 封装的代码如下:
  • import preferences from '@ohos.data.preferences'
    import { JSON } from '@kit.ArkTS'
    import { BusinessError } from '@kit.BasicServicesKit'
    
    const TAG: string = 'PreferencesUtil'
    
    /**
     * 用户持久化工具类封装
     *
     * @author weipeng
     * @since 2025-01-04
     */
    class PreferencesUtil {
      prefMap: Map<string, preferences.Preferences> = new Map
    
      async loadPreference(context: Context, name: string) {
        try {
          // 加载preferences
          let pref = await preferences.getPreferences(context, name)
          this.prefMap.set(name, pref)
          console.log(TAG, `加载Preferences[${name}]成功`)
        } catch (exception) {
          console.log(TAG, `加载Preferences[${name}]失败`, JSON.stringify(exception))
        }
      }
    
      async putPreferencesValue(name: string, key: string, value: preferences.ValueType) {
        let pref = this.prefMap.get(name)
        if (!pref) {
          console.log(TAG, `Preferences[${name}]尚未初始化!`)
          return
        }
        try {
          // 写入数据
          await pref.put(key, value)
          // 刷盘
          await pref.flush()
          console.log(TAG, `保存Preferences[${name}.${key} = ${value}]成功`)
        } catch (exception) {
          console.log(TAG, `保存Preferences[${name}.${key} = ${value}]失败`, JSON.stringify(exception))
        }
      }
    
      async getPreferencesValue(name: string, key: string, defaultValue: preferences.ValueType) {
        let pref = this.prefMap.get(name)
        if (!pref) {
          console.log(TAG, `Preferences[${name}]尚未初始化!`)
          return
        }
        try {
          // 读数据
          let value = await pref.get(key, defaultValue)
          console.log(TAG, `获取Preferences[${name}.${key} = ${value}]成功`)
        } catch (exception) {
          console.log(TAG, `获取Preferences[${name}.${key} ]失败`, JSON.stringify(exception))
        }
      }
    
      deletePreferencesKeyValue(name: string, key: string) {
        let pref = this.prefMap.get(name)
        if (!pref) {
          console.log(TAG, `Preferences[${name}]尚未初始化!`)
          return
        }
        try {
          // 删除指定键值对
          let value = pref.deleteSync(key)
          console.log(TAG, `删除Preferences[${name}.${key} = ${value}]成功`)
        } catch (exception) {
          console.log(TAG, `删除Preferences[${name}.${key} ]失败`, JSON.stringify(exception))
        }
      }
    
      deletePreferences(context: Context, name: string) {
        preferences.deletePreferences(context, name, (err: BusinessError) => {
          if (err) {
            console.error(TAG, `Failed to delete preferences. Code:${err.code}, message:${err.message}`)
            return
          }
          console.info(TAG, 'Succeeded in deleting preferences.')
          this.prefMap.delete(name)
        })
      }
    }
    
    const preferencesUtil: PreferencesUtil = new PreferencesUtil()
    
    export default preferencesUtil as PreferencesUtil

通过关系型数据库实现数据持久化

场景介绍

关系型数据库基于SQLite组件,适用于存储包含复杂关系数据的场景,比如一个班级的学生信息,需要包括姓名、学号、各科成绩等,又或者公司的雇员信息,需要包括姓名、工号、职位等,由于数据之间有较强的对应关系,复杂程度比键值型数据更高,此时需要使用关系型数据库来持久化保存数据。

大数据量场景下查询数据可能会导致耗时长甚至应用卡死,如有相关操作可参考文档批量数据写数据库场景,且有建议如下:

运作机制

关系型数据库对应用提供通用的操作接口,底层使用SQLite作为持久化存储引擎,支持SQLite具有的数据库特性,包括但不限于事务、索引、视图、触发器、外键、参数化查询和预编译SQL语句。

图1 关系型数据库运作机制

  • 单次查询数据量不超过5000条。
  • 在TaskPool中查询。
  • 拼接SQL语句尽量简洁。
  • 合理地分批次查询。
  • 基本概念

  • 谓词:数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。

  • 结果集:指用户查询之后的结果集合,可以对数据进行访问。结果集提供了灵活的数据访问方式,可以更方便地拿到用户想要的数据。

约束限制

系统默认日志方式是WAL(Write Ahead Log)模式,系统默认落盘方式是FULL模式。

数据库中有4个读连接和1个写连接,线程获取到空闲读连接时,即可进行读取操作。当没有空闲读连接且有空闲写连接时,会将写连接当做读连接来使用。

为保证数据的准确性,数据库同一时间只能支持一个写操作。

当应用被卸载完成后,设备上的相关数据库文件及临时文件会被自动清除。

接口说明

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

接口名称描述
getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback<RdbStore>): void获得一个RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口可以执行相关的数据操作。
executeSql(sql: string, bindArgs: Array<ValueType>, callback: AsyncCallback<void>):void执行包含指定参数但不返回值的SQL语句。
insert(table: string, values: ValuesBucket, callback: AsyncCallback<number>):void向目标表中插入一行数据。
update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback<number>):void根据predicates的指定实例对象更新数据库中的数据。
delete(predicates: RdbPredicates, callback: AsyncCallback<number>):void根据predicates的指定实例对象从数据库中删除数据。
query(predicates: RdbPredicates, columns: Array<string>, callback: AsyncCallback<ResultSet>):void根据指定条件查询数据库中的数据。
deleteRdbStore(context: Context, name: string, callback: AsyncCallback<void>): void

删除数据库。

    • ArkTS侧支持的基本数据类型:number、string、二进制类型数据、boolean。

    • 为保证插入并读取数据成功,建议一条数据不要超过2M。超出该大小,插入成功,读取失败。

    • 封装的代码如下:

    • bean:

    • @Observed
      export default  class TaskInfo {
        id: number
        name: string
        finished: boolean
      
        constructor(id: number, name: string) {
          this.id = id
          this.name = name
          this.finished = false
        }
      }
import { relationalStore } from '@kit.ArkData'; // 导入模块
import TaskInfo from './TaskInfo';

const TAG: string = 'DbUtil'

/**
 * 关系型DB工具类封装
 *
 * @author weipeng
 * @since 2025-01-04
 */
class DbUtil {
  private rgbStore: relationalStore.RdbStore | undefined
  private tableName:string = 'TASK'

  initDb(context: Context) {
    // 1、rdb配置
    // customDir: 'customDir/subCustomDir', // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建
    // context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,
    // '/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例。
    const config: relationalStore.StoreConfig = {
      name: 'wp.db', // 数据库文件名
      securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别
      encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
      isReadOnly: false // 可选参数,指定数据库是否以只读方式打开。该参数默认为false,表示数据库可读可写
    }

    // 2、建表Sql语句, IDENTITY为bigint类型,sql中指定类型为UNLIMITED INT
    const sql = `CREATE TABLE IF NOT EXISTS TASK (ID INTEGER PRIMARY KEY AUTOINCREMENT
       NAME TEXT NOT NULL, FINISHED bit)`;

    // 3、获取rgb
    relationalStore.getRdbStore(context, config, (err, rgbStore) => {
      if (err) {
        console.log(TAG, '获取rgbStore失败')
        return
      }
      // 执行sql
      rgbStore.executeSql(sql)
      console.log(TAG, '创建task表成功!')
      // 保存rgbStore
      this.rgbStore = rgbStore
    })
  }

  /**
   * 查询
   */
  async getTaskList() {
    // 1、构建查询条件
    let predicates = new relationalStore.RdbPredicates(this.tableName)
    // 2、查询
    let result = await this.rgbStore?.query(predicates, ['ID', 'NAME', 'FINISHED'])
    // 3、解析查询结果
    // 3.1 定义一个数组,组装最终的查询结果
    let tasks: TaskInfo[] = []
    // 3.2 遍历封装
    while(!result?.isAtLastRow) {
      // 3.3 指针移动到下一行
      result?.goToNextRow()
      let id = result?.getLong(result.getColumnIndex('ID')) as number
      let name = result?.getString(result.getColumnIndex('NAME'))
      let finished = result?.getLong(result.getColumnIndex('FINISHED'))
      // 3.4 封装到数组
      tasks.push(new TaskInfo(id, ''))
    }
    return tasks
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/951239.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SOLID原则学习,开闭原则

文章目录 1. 定义2. 开闭原则的详细解释3. 实现开闭原则的方法4. 总结 1. 定义 开闭原则&#xff08;Open-Closed Principle&#xff0c;OCP&#xff09;是面向对象设计中的五大原则&#xff08;SOLID&#xff09;之一&#xff0c;由Bertrand Meyer提出。开闭原则的核心思想是…

西电-算法分析-研究生课程复习笔记

24年秋的应该是张老师最后一次用卷面考试&#xff0c;他说以后这节课的期末考试都是在OJ上刷题了张老师上课还挺有意思的&#xff0c;上完之后能学会独立地思考算法设计问题了。整节课都在强调规模压缩这个概念&#xff0c;考试也是考个人对这些的理解&#xff0c;还挺好玩的哈…

插入实体自增主键太长,mybatis-plaus自增主键

1、问题 spring-boot整合mybtais执行insert语句时&#xff0c;主键id为长文本数据。 2、分析问题 1)数据库主键是否自增 2&#xff09;数据库主键的种子值设置的多少 3、解决问题 1&#xff09;数据库主键设置的时自增 3&#xff09;种子值是1 所以排查是数据库的问题 4、继…

上海亚商投顾:沪指探底回升微涨 机器人概念股午后爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 市场全天探底回升&#xff0c;沪指盘中跌超1.6%&#xff0c;创业板指一度跌逾3%&#xff0c;午后集体拉升翻红…

基于深度学习算法的AI图像视觉检测

基于人工智能和深度学习方法的现代计算机视觉技术在过去10年里取得了显著进展。如今&#xff0c;它被广泛用于图像分类、人脸识别、图像中物体的识别等。那么什么是深度学习&#xff1f;深度学习是如何应用在视觉检测上的呢&#xff1f; 什么是深度学习&#xff1f; 深度学习是…

基于Spring Boot的海滨体育馆管理系统的设计与实现

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的海滨体育馆管理系统的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 宠物医院…

深度学习每周学习总结R3(LSTM-火灾温度预测)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客R4中的内容&#xff0c;为了便于自己整理总结起名为R3&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结1. LSTM介绍LSTM的基本组成部分如何理解与应用LSTM 2. 数据导入3. 数据…

全方位解读消息队列:原理、优势、实例与实践要点

全方位解读消息队列&#xff1a;原理、优势、实例与实践要点 一、消息队列基础认知 在数字化转型浪潮下&#xff0c;分布式系统架构愈发复杂&#xff0c;消息队列成为其中关键一环。不妨把消息队列想象成一个超级“信息驿站”&#xff0c;在古代&#xff0c;各地的信件、物资运…

conda install包时出现CondaHTTPError: HTTP 403 FORBIDDEN for url ....问题,但已经排除镜像源问题

最近连WIFI下包出现如下问题&#xff0c;已排除镜像源问题。但是一直装不上包。 CondaHTTPError: HTTP 403 FORBIDDEN for url https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/win-64/ca-certifica Elapsed: 00:00.202308 An HTTP error occurred when trying to …

【Rust自学】11.3. 自定义错误信息

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 11.3.1. 添加错误信息 在 11.2. 断言(Assert) 中我们学习了assert!、assert_eq!和assert_ne!这三个宏&#xff0c;而这篇文章讲的就是它…

linux下shell中使用上下键翻出历史命名时出现^[[A^[[A^[[A^[[B^[[B的问题解决

前言 今天在使用linux的时候&#xff0c;使用上下键想翻出历史命令时&#xff0c;却出现[[A[[A[[A[[B^[[B这种东东&#xff0c;而tab键补全命令的功能也无法使用。最终发现是由于当前用户使用的shell是/bin/sh的原因。 解决方法 运行以下命令&#xff0c;将默认 shell 设置为…

【操作系统】课程 8文件管理 同步测练 章节测验

8.1知识点导图 它详细地展示了文件的定义、分类、逻辑结构、目录结构以及文件共享和保护的各个方面。下面是对图中内容的文字整理&#xff1a; 文件定义 文件是具有文件名的一组相关信息的集合。 文件分类 按用途分类&#xff1a;系统文件、用户文件、库文件。按存取控制属性分…

1月9日星期四今日早报简报微语报早读

1月9日星期四&#xff0c;农历腊月初十&#xff0c;早报#微语早读。 1、上海排查47家“俄罗斯商品馆”&#xff1a;个别店铺被责令停业&#xff0c;立案调查&#xff1b; 2、西藏定日县已转移受灾群众4.65万人&#xff0c;检测到余震646次&#xff1b; 3、国家发改委&#x…

1.8-9号Python猛刷动态规划

今日宽恕:总结不是纠结过去&#xff0c;表达不是“见斑知豹”&#xff0c;还要更多信息整合后去回答。 题目一 3297.统计重新排列后包含另一个字符串| 示例 1&#xff1a; 输入&#xff1a;word1 "abcabc", word2 "abc" 输出&#xff1a;10 解释&#…

【Python】论文长截图、页面分割、水印去除、整合PDF

有的学校的论文只能在线预览&#xff0c;且存在水印。为保存到本地方便查阅&#xff0c;可以使用以下工作流进行处理&#xff1a; 用浏览器打开在线论文预览界面&#xff1b;使用fastone capture软件截长图&#xff1b;将论文按页数进行分割&#xff1b;按照阈值消除浅色的背景…

FPGA的 基本结构(Xilinx 公司Virtex-II 系列FPGA )

以Xilinx 公司Virtex-II 系列FPGA 为例&#xff0c;其基本结构由下图所示。它是主要由两大部分组成&#xff1a;可编程输入/输出&#xff08;Programmable I/Os&#xff09;部分和内部可配置&#xff08;Configurable Logic&#xff09;部分。 可编程输入/输出&#xff08;I/Os…

详解Sonar与Jenkins 的集成使用!

本文阅读前提 本文假设读者熟悉Jenkins和SonarQube的基础操作。 核心实现功能 Jenkins中运行的job来调用SonarScanner&#xff0c;最后可实现测试结果与SonarQube中同步查看。 Jenkins中安装Sonar相关插件 配置Sonarqube Dashboard>Manage Jenkins>Systems 指定son…

010:传统计算机视觉之大津算法初探

本文为合集收录&#xff0c;欢迎查看合集/专栏链接进行全部合集的系统学习。 合集完整版请参考这里。 上一节学习了利用 Canny 算法来完成一个图片的边缘检测&#xff0c;从而可以区分出图像的边缘。 本节再了解一个计算机视觉中更常见的应用&#xff0c;那就是把图片的前景和…

Harmony开发-ArkUI框架速成十一Swiper布局

程序员Feri一名12年的程序员,做过开发带过团队创过业,擅长Java、嵌入式、鸿蒙、人工智能等,专注于程序员搞钱那点儿事,希望在搞钱的路上有你相伴&#xff01;君志所向,一往无前&#xff01; 1.Swiper 1.1 Swiper组件 Swiper组件提供滑动轮播显示的能力。 Swiper本身是一个容…

怎么抓取ios 移动app的https请求?

怎么抓取IOS应用程序里面的https&#xff1f; 这个涉及到2个问题 1.电脑怎么抓到IOS手机流量&#xff1f; 2.HTTPS怎么解密&#xff1f; 部分app可以使用代理抓包的方式&#xff0c;但是正式点的app用代理抓包是抓不到的&#xff0c;例如pin检测&#xff0c;证书双向校验等…