第5章 数据存储与访问 数据存储是开发应用程序时需要解决的最基本问题,虽然客户端App 的数据一般都由 云端(服务端)提供,但是随着移动互联网的发展,用户对应用程序的性能、体验等各方面要 求都有所增强,目前客户端App 一般都需要支持离线使用模式,因此在进行客户端应用程 序开发时需要客户端实现本地数据的存储与访问。本章结合具体案例介绍方舟开发框架提 供的JSAPI 实现本地存储与访问数据的机制,以便读者全面地了解本地数据存储与访问接 口,更好地开发HarmonyOS 应用程序。 5.概述 1 HarmonyOS 应用程序开发的数据管理机制包括轻量级数据存储(偏好文件)、文件、关扫一扫 系数据库、对象关系映射数据库、分布式数据服务和分布式文件服务等。华为官方推出的 JSAPI 提供了一系列完整的配套接口,用来实现HarmonyOS 应用程序开发中的数据存储 和访问机制。读者需要注意,JSAPI 中的部分数据存储与访问接口从APIversion6 开始 华为官方不再维护,所以推荐使用官方提供的最新接口开发HarmonyOS 应用程序。 5.1 轻量级数据存储与访问机制 1. 在HarmonyOS 应用程序开发中,轻量级数据存储以key-value结构的形式对数据进行 存取操作。数据存储形式为键值对,键(key)是不重复的关键字,它的类型为字符串型;值 (value)是数据值,它存储数据的类型包括数字型、字符型、布尔型。应用程序在获取某个轻 量级数据存储对象后,该存储对象中的数据将会被缓存在内存中,以便应用程序以更快的速 度实现数据访问,提高效率。当然,应用程序也可以将缓存的数据写入HarmonyOS 终端设 备存储器的文本文件中进行持久化存储。由于文件读写会产生不可避免的系统资源开销, 因此开发者在开发应用程序时,应尽量减少对存放在HarmonyOS 终端设备存储器中文本 文件的读写频率。轻量级数据存储访问机制适用于应用程序在本地存储少量数据,经常应 用于操作键值对形式数据的场景。从APIverin6 开始,JSAPI 提供的@oho.aa sosdt. storage接口可以实现数据存储对象的获取和写入。 1.文件存储与访问机制 5.2 文件通常应用于将数据以普通文件格式下载或保存到HarmonyOS 终端设备的本地存 储空间。从APIvesoJSAPI 提供的@ohsfli rin6 开始,o.ieo接口可以实现对文件或目录进 行操作。但是,在操作文件或目录前,必须先通过Ability上下文提供的相关接口,获取操作 对象在内部存储目录或内部存储缓存目录的绝对路径,然后才能进行文件或目录的创建、打 开、读取、写入、复制、删除、重命名、修改操作权限、修改所有者等操作。 1.关系数据库存储与访问机制 5.3 关系数据库(RelationalDatabase,RDB)是一种基于关系模型管理数据的数据库。 HarmonyOS 关系数据库基于SQLite组件提供了一套完整的对本地数据库进行管理的机 制,对外提供了一系列的增、删、改、查等接口,也可以直接运行用户输入的SQL(Structured QueryLanguage,结构化查询语言)语句来满足复杂的场景需要。SQLite是一款轻量级的 关系数据库管理系统,它既支持事务和批处理、自动版本管理、标准的CURD(Create、 Update、Retrieve、Delete)操作,也遵守ACID(Atomic、Consistency、Isolation、Durability)特 性。HarmonyOS 提供的关系数据库功能更加完善,查询效率更加高效,从APIversion7 开 始,JSAPI 提供的@oho.aar sdt.db 接口可以实现关系数据的操作。 1.对象关系映射数据库存储与访问机制 5.4 对象关系映射(ObjectRelationalMapping,ORM)数据库是一款基于SQLite的数据库 框架,屏蔽了底层SQLite数据库的SQL 操作,针对实体和关系提供了增、删、改、查等一系 列的面向对象接口。在进行HarmonyOS 应用程序开发时,开发者不必再编写复杂的SQL 语句,而是直接以操作对象的形式操作数据库,所以在提升开发效率的同时,也能让开发者 聚焦于业务开发。对象关系映射数据库跟关系数据库一样,都使用SQLite作为持久化引 擎,底层使用的是同一套数据库连接池和数据库连接机制。对象关系映射数据库适用于开 发者使用的数据可以分解为一个或多个对象,且需要对数据进行增、删、改、查等操作,但是 不希望编写过于复杂的SQL 语句的场景。 5.睡眠质量测试系统的设计与实现 2 如今睡眠已经成为全世界人们共同关注的问题。由于长期睡眠质量不高,常会伴随着 多种疾病的产生,其中最突出的就是精神类疾病,睡眠障碍是它的主要表现形式。而大多数 人对睡眠认识不够,往往忽略了睡眠与身体健康的关系,这样就造成很多睡眠质量不高的人 错失了治疗的最佳时间,因此就需要提供一个方便快捷的方法来帮助这些人测量自己的睡 眠状况,让他们尽早得到相应的治疗和帮助。本节以国际公认的睡眠质量自测量表———阿 森斯失眠量表为理论依据,结合switch组件、stepper组件、stepper-item 组件、页面路由和 139 轻量级数据存储与访问机制设计并实现一个睡眠质量测试系统。 5.1 sich组件 2.wt switch组件(开关选择器组件)用于开启或关闭某个功能,除支持通用属性和通用事件 外,还支持如表5.2所示的事件。扫一扫 1所示的属性和如表5. 表5.wth组件属性及功能 1 sic 属性名类型功能说明 checked boolean 设置开关是否选中,属性值包括false(默认值,未选中)和true showtext boolean 设置开关是否显示文本,属性值包括false(默认值,不显示)和true texton string 设置开关选中时显示的文本,默认值为On textof string 设置开关选中时显示的文本,默认值为Of 表5.h组件事件及功能 2 switc change 事件名返回值功能说明 {checked:checkedValue} 开关选择器选中状态改变时,触发该回调事件 -witc1 所示。 【范例51】用sh组件在页面上实现一个模拟开灯、关灯的效果,运行效果如图5. 图5.开关选择器效果 1 1 40 css的代码如下。 1 .container { 2 display: flex; 3 flex-direction: column; 4 align-items: center; 5 margin: 15fp; 6 width: 100%; 7 height: 100%; 8 } 9 .switch-css { 10 texton-color: blue; 11 textoff-color: red; 12 font-size: 25fp; 13 } 上述第9~13行代码定义switch组件的样式,texton-color样式表示开关选择器处于 选中状态时显示的文本颜色,textoff-color样式表示开关选择器处于未选中状态时显示的 文本颜色。 hml的代码如下。 1
2 3 4
上述第2行代码用image组件加载代表灯亮的图片(light.png)和代表灯灭的图片 (black.png),这两张图片需要先复制到项目的common/images文件夹中。 js的代码如下。 1 export default { 2 data: { 3 imgSrc:'/common/images/black.png', //默认加载代表灯灭的图片 4 isChecked:false //默认开关选择器未选中 5 }, 6 controlLamp(e){ 7 if(e.checked){ 8 this.imgSrc = '/common/images/light.png' //加载代表灯亮的图片 9 }else{ 10 this.imgSrc = '/common/images/black.png' //加载代表灯灭的图片 11 } 12 } 13 } 上述第7~11行代码表示如果开关选择器处于选中状态,则加载代表灯亮的图片文件 (light.png),否则加载代表灯灭的图片文件(black.png)。 141 5.2 轻量级数据存储与访问接口 2. 轻量级数据存储为应用程序提供key-value(键值对)类型的文件数据处理能力,支持应 用程序对数据进行轻量级的存储及查询。数据存储形式为键值对,键的类型为字符串型,最 大长度为80B;值的存储数据类型包括数字型、字符型和布尔型,其中字符型值的最大长度 为8192B 。在HarmonyOS 应用程序开发中,借助JSAPI 提供的@ohsdatstoae接口 o.a.rg 可以实现数据存储对象的获取和写入,写入的数据保存在HarmonyOS 终端设备存储器的 文本文件中。 1. 读取指定文件 .dataStorae.etStoraenc(path:string):Storae,同步读取指定文件,并将数据 gggSygc参数及功能说明如表5. g 加载到 etStoraSgteoSryangce 返回值类型及功能说明如表 类型的实例。getStorageSy5. n4所示。 3所示。 扫一扫 表5.c参数及功能说明 3 getStorageSyn 参数名类型必填功能说明 path string 是设置应用程序内部数据存储路径 表5.c返回值类型及功能说明 4 getStorageSyn 返回值类型功能说明 返回Storage类型实例,可用于数据存储及查询操作 【范例5-2】用同步方式读取应用程序内部数据存储路径下的cofg.ni文件,并将 Storage nii cofg.ni文件的存储路径显示在页面上,运行效果如图5. nii2所示。 图5.同步读取文件目录路径 2 1 42 hml的代码如下。 1
2 config.ini 文件的存储路径: {{ pathName }} 3 4
上述第1行代码引用的container样式类代码内容与范例5-1的container样式类代码 完全一样,限于篇幅,这里不再赘述。 js的代码如下。 1 import dataStorage from '@ohos.data.storage' 2 import featureAbility from '@ohos.ability.featureAbility' 3 export default { 4 data: { 5 pathName: " //保存config.ini 文件的存放目录 6 storage:" //保存数据存储实例 7 }, 8 async openConfig() { 9 var context = featureAbility.getContext() //获取上下文 10 var path = await context.getFilesDir() //获得应用程序内部存储目录 11 this.storage = dataStorage.getStorageSync(path + '/config.ini') //读取config.ini 文件 12 this.pathName = path 13 //操作config.ini 文件中的数据 14 } 15 } HarmonyOS应用程序使用轻量级数据存储与访问机制存取数据时,必须首先从 @ohos.data.storage接口中导入dataStorage包,即上述第1行代码;然后引用该包中的各 种方法对轻量级数据进行操作。轻量级数据存储与访问实际上就是对内部存储器上以键值 对格式存储的文本文件进行操作,所以在读取文件时必须指明文件的存放位置,即上述第 2行及9~10行代码,其中第2行代码表示从@ohos.ability.featureAbility接口中导入 featureAbility包,第9~10行代码表示调用该包中的getContext()方法获得Ability上下 文;再通过Ability上下文调用getFilesDir()方法异步获取应用程序在内部存储器上的文件 路径。由于getFilesDir()方法是异步(async)执行的,所以上述第8~14行代码定义了一个 异步事件,其中第10行代码中的await表示只有在获得应用程序内部存储目录后,才能执 行第11行及后面的代码。虽然异步事件会返回一个Promise对象,但代码前用await关键 字表示可以等待异步事件的返回值,也就是说,await不仅可以用于等待返回Promise对象, 而且它还可以等待任意表达式的结果,所以上述第10行代码直接将返回的值赋给path变 量。从图5.2可以看出,getFilesDir()方法返回的应用程序在内部存储器上的文件路径为 “/data/data/应用程序包名/files”。如果将上述第10行代码修改为下述代码,则表示获取 应用程序内部存储器上的缓存目录路径,运行效果如图5.3所示。getCacheDir()方法返回 1 43 的应用程序在内部存储器上的缓存目录路径为“/data/data/应用程序包名/cache”。 图5.3 同步读取缓存目录路径 1 var path = await context.getCacheDir() //获得应用程序内部存储缓存目录 .dataStorage.getStorage(path:string,callback:AsyncCallback):void, 异步读取指定文件,并将数据加载到Storage类型的实例,以callback形式返回结 果。getStorage参数及功能说明如表5.5所示。 表5.5 getStorage参数及功能说明 参数名类 型必填功能说明 path string 是设置应用程序内部数据存储路径 callback AsyncCallback 是回调函数 例如,要实现范例5-2的功能,也可以将范例5-2的第11~13行js代码修改为如下 代码。 1 var that = this 2 dataStorage.getStorage(path + '/config.ini', function (err, storage) { 3 if (err) { 4 that.pathName ="读内部存储文件失败" 扫一扫 1 44 5 return; 6 } 7 //用storage 实例操作config.ini 文件中的数据 8 that.pathName = path 9 }) . dataStorage.getStorage(path:string):Promise,异步读取指定文件,并 将数据加载到Storage 实例。getStorage 参数及功能说明如表5.3 所示。 getStorage返回值类型及功能说明如表5.6所示。 表5.6 getStorage返回值类型及功能说明 返回值类型功能说明 Promise 返回Promise实例,用于异步获取结果 例如,要实现范例5-2的功能,也可以将范例5-2的第11~13行js代码修改为如下 代码。 1 var that = this 2 let promise = dataStorage.getStorage(path + '/config.ini') 3 promise.then((storage) => { 4 //用storage 实例操作config.ini 文件中的数据 5 that.pathName = path 6 }).catch((err) => { 7 that.pathName = "读内部存储文件失败" 8 }) 2.向指定存储对象写数据 . putSync(key:string,value:ValueType):void,同步将数据写入Storage类型的实 例,并用flush()或flushSync()方法将Storage类型的实例写入指定文件保存。 putSync参数及功能说明如表5.7所示。 表5.7 putSync参数及功能说明 参数名类 型必填功能说明 key string 是设置要存储或修改的键,不能为空 value number、string或boolean 是设置要存储或修改的值 【范例5-3】 用同步方式将登录用户名(key为loginName,value为nipaopao)和登录 密码(key为loginPwd,value为123456)以键值对方式保存在应用程序内部存储路径下的 config.ini文件中。 在实现范例5-2的基础上,在hml的代码中增加1个“保存数据”按钮,其代码如下。 1 扫一扫 1 45 在实现范例5-2的基础上,在js的代码中增加1个“保存数据”单击事件的回调方法,其 代码如下。 1 writeConfig() { 2 this.storage.putSync('loginName', 'nipaopao') //key 为'loginName',value 为'nipaopao' 3 this.storage.putSync('loginPwd', '123456') //key 为'loginPwd',value 为'123456' 4 this.storage.flushSync() / /保 存 到 文 件 中 5 console.info("保存成功") 6 } . put(key:string,value:ValueType,callback:AsyncCallback):void,异 步将数据写入Storage类型的实例,并用flush()或flushSync()方法将Storage类型 的实例写入指定文件保存。put参数及功能说明如表5.8所示。 表5.8 put参数及功能说明 参数名类 型必填功能说明 key string 是设置要存储或修改的键,不能为空 value number、string或boolean 是设置要存储或修改的值 callback AsyncCallback 是回调函数 例如,要实现范例5-3的功能,也可以将“保存数据”单击事件的回调方法修改为如下 代码。 1 writeConfig(){ 2 var that = this 3 this.storage.put('loginName', 'nipaopao', function (err) { //保存登录用户名 4 if (err) { 5 console.info("保存失败:" + err) 6 return 7 } 8 that.storage.flushSync() 9 console.info("保存成功") 10 }) 11 //保存登录密码的代码类似,此处略 12 } . put(key:string,value:ValueType):Promise,异步将数据写入Storage 类型的实例,并用flush()或flushSync()方法将Storage类型的实例写入指定文件 保存。put参数及功能说明如表5.8所示。put返回值类型及功能说明如表5.9 所示。 扫一扫 1 46 表5.9 put返回值类型及功能说明 返回值类型功能说明 Promise 返回Promise实例,用于异步处理 例如,要实现范例5-3的功能,也可以将“保存数据”单击事件的回调方法修改为如下代码。 1 writeConfig() { 2 let promise = this.storage.put('loginName', 'nipaopao') 3 promise.then(() => { 4 console.info("保存成功") 5 }).catch((err) => { 6 console.info("保存失败:" + err) 7 }) 8 //保存登录密码的代码类似,此处略 9 } 3.从指定存储对象中读数据 . getSync(key:string,defValue:ValueType):ValueType,同步获取指定键的值,如果值 为null或者非默认值类型,则返回默认数据。getSync参数及功能说明如表5.10所示。 表5.10 getSync参数及功能说明 参数名类 型必填功能说明 key string 是设置要获取的键,不能为空 defValue number、string或boolean 是若给定的键不存在,则设置该键返回的默认值 【范例5-4】 用同步方式从应用程序内部存储路径下的config.ini文件中读出登录用户名 (key为loginName)和登录密码(key为loginPwd),并显示在如图5.4所示页面的对应位置。 图5.4 同步读出轻量级数据 扫一扫