《鸿蒙next ArkUI专栏》系列前言:

效果如图



#HarmonyOS NEXT体验官#  鸿蒙next 数据持久化 你不知道的事情_数据

#HarmonyOS NEXT体验官#  鸿蒙next 数据持久化 你不知道的事情_首选项_02

分享内容:

鸿蒙next中应用数据持久化

鸿蒙的应用数据持久化有3种
1 通过用户首选项实现数据持久化
2 通过键值型数据库实现数据持久化
3 通过关系型数据库实现数据持久化

今天我们主要讲的是 通过用户首选项实现数据持久化后面的两种方式我们后面的文章也会陆续的讲到。

场景介绍

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

运作机制

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

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

图1 用户首选项运作机制

约束限制

  • Key键为string类型,要求非空且长度不超过80个字节。
  • 如果Value值为string类型,请使用UTF-8编码格式,可以为空,不为空时长度不超过8192个字节。
  • 内存会随着存储数据量的增大而增大,所以存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销

接口说明

** 接口名称

描述

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

将当前Preferences实例的数据异步存储到用户首选项持久化文件中。

on(type: 'change', callback: Callback): void

订阅数据变更,订阅的数据发生变更后,在执行flush方法后,触发callback回调。

off(type: 'change', callback?: Callback): void

取消订阅数据变更。

deletePreferences(context: Context, options: Options, callback: AsyncCallback): void

从内存中移除指定的Preferences实例。若Preferences实例有对应的持久化文件,则同时删除其持久化文件。**

具体开发步骤

1 导入@kit.ArkData模块
import { preferences } from '@kit.ArkData';
2 获取Preferences实例。
Stage模型示例:
import { window } from '@kit.ArkUI';
 let dataPreferences: preferences.Preferences | null = null;
 let options: preferences.Options = { name: 'myStore' };
 dataPreferences = preferences.getPreferencesSync(this.context, options);
FA模型示例:
// 获取context
import { featureAbility } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

let context = featureAbility.getContext();
let options: preferences.Options =  { name: 'myStore' };
let dataPreferences: preferences.Preferences = preferences.getPreferencesSync(context, options);

3 写入数据:

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

import { util } from '@kit.ArkTS';
if (dataPreferences.hasSync('startup')) {
console.info("The key 'startup' is contained.");
} else {
console.info("The key 'startup' does not contain.");
// 此处以此键值对不存在时写入数据为例
dataPreferences.putSync('startup', 'auto');
 dataPreferences!!.flush()
}
4 读取数据

使用getSync()方法获取数据,即指定键对应的值。如果值为null或者非默认值类型,则返回默认数据

let val = dataPreferences.getSync('startup', 'default');
console.info("The 'startup' value is " + val);
5 删除数据

使用deleteSync()方法删除指定键值对,示例代码如下所示

dataPreferences.deleteSync('startup');
6 数据持久化

应用存入数据到Preferences实例后,可以使用flush()方法实现数据持久化

dataPreferences.flush((err: BusinessError) => {
 if (err) {
  console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
  return;
 }
 console.info('Succeeded in flushing.');
 })
7 订阅数据变更

应用订阅数据变更需要指定observer作为回调方法。订阅的Key值发生变更后,当执行flush()方法时,observer被触发回调。示例代码如下所示:

let observer = (key: string) => {
  console.info('The key' + key + 'changed.');
  }
  dataPreferences.on('change', observer);
  // 数据产生变更,由'auto'变为'manual'
  dataPreferences.put('startup', 'manual', (err: BusinessError) => {
 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'.");
  if (dataPreferences !== null) {
  dataPreferences.flush((err: BusinessError) => {
  if (err) {
 console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
  return;
 }
  console.info('Succeeded in flushing.');
  })
  }
  })
8 删除指定文件

使用deletePreferences()方法从内存中移除指定文件对应的Preferences实例,包括内存中的数据。若该

Preference存在对应的持久化文件,则同时删除该持久化文件,包括指定文件及其备份文件、损坏文件
  preferences.deletePreferences(this.context, options, (err: BusinessError) => {
  if (err) {
  console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
  return;
  }
  console.info('Succeeded in deleting preferences.');
  })

项目中实战运用:

上面的 1-8 条我也是参考了官方的讲解但是实战开发中直接复制官方代码肯定代码封装的不好 也不利于服用,这边就写了一个工具类

import Logger from './Logger'
import common from '@ohos.app.ability.common'
import { preferences } from '@kit.ArkData'

/***
*
* 创建人:xuqing
* 创建时间:2024年7月15日12:38:29
* 类说明:preference 工具类
*
*
*/

let preference: preferences.Preferences | undefined = undefined
const context = getContext(this) as common.UIAbilityContext

export class PreferencesUtil {
 private readonly TAG: string = 'PreferenceUtil';

 writeString(key: string, value?: string) {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.putSync(key, value)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to write value, cause:' + e)
   }
   preference!!.flush()
 }

 readString(key: string, defaultValue?: string): string | undefined {
   if (preference === undefined) {
     this.getPreferences()
   }
   let value: preferences.ValueType | undefined = undefined
   try {
     value = preference!!.getSync(key, defaultValue)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to read value, cause:' + e)
   }
   return value as string | undefined
 }

 writeArrayString(key: string, value?: Array<string>) {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.putSync(key, value)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to write value, cause:' + e)
   }
   preference!!.flush()
 }

 readArrayString(key: string, defaultValue?: Array<string>): Array<string> | undefined {
   if (preference === undefined) {
     this.getPreferences()
   }
   let value: preferences.ValueType | undefined = undefined
   try {
     value = preference!!.getSync(key, defaultValue)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to read value, cause:' + e)
   }
   return value as Array<string> | undefined
 }

 writeNumber(key: string, value?: number) {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.putSync(key, value)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to write value, cause:' + e)
   }
   preference!!.flush()
 }

 readNumber(key: string, defaultValue?: number): number | undefined {
   if (preference === undefined) {
     this.getPreferences()
   }
   let value: preferences.ValueType | undefined = undefined
   try {
     value = preference!!.getSync(key, defaultValue)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to read value, cause:' + e)
   }
   return value as number | undefined
 }

 writeArrayNumber(key: string, value?: Array<number>) {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.putSync(key, value)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to write value, cause:' + e)
   }
   preference!!.flush()
 }

 readArrayNumber(key: string, defaultValue?: Array<number>): Array<number> | undefined {
   if (preference === undefined) {
     this.getPreferences()
   }
   let value: preferences.ValueType | undefined = undefined
   try {
     value = preference!!.getSync(key, defaultValue)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to read value, cause:' + e)
   }
   return value as Array<number> | undefined
 }

 writeBoolean(key: string, value?: boolean) {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.putSync(key, value)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to write value, cause:' + e)
   }
   preference!!.flush()
 }

 readBoolean(key: string, defaultValue?: boolean): boolean | undefined {
   if (preference === undefined) {
     this.getPreferences()
   }
   let value: preferences.ValueType | undefined = undefined
   try {
     value = preference!!.getSync(key, defaultValue)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to read value, cause:' + e)
   }
   return value as boolean | undefined
 }

 writeArrayBoolean(key: string, value?: Array<boolean>) {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.putSync(key, value)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to write value, cause:' + e)
   }
   preference!!.flush()
 }

 readArrayBoolean(key: string, defaultValue?: Array<boolean>): Array<boolean> | undefined {
   if (preference === undefined) {
     this.getPreferences()
   }
   let value: preferences.ValueType | undefined = undefined
   try {
     value = preference!!.getSync(key, defaultValue)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to read value, cause:' + e)
   }
   return value as Array<boolean> | undefined
 }

 writeObject(key: string, value?: object) {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.putSync(key, value)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to write value, cause:' + e)
   }
   preference!!.flush()
 }

 readObject<T>(key: string): T | undefined {
   if (preference === undefined) {
     this.getPreferences()
   }
   let value: preferences.ValueType | undefined = undefined
   try {
     value = preference!!.getSync(key, undefined)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to read value, cause:' + e)
   }
   if (value === undefined) {
     return undefined
   }else {
     return value as T
   }
 }

 delete(key: string) {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.deleteSync(key)
   } catch (e) {
     Logger.error(this.TAG, 'Failed to delete, cause:' + e)
   }
   preference!!.flush()
 }

 clear() {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.clearSync()
   } catch (e) {
     Logger.error(this.TAG, 'Failed to clear, cause:' + e)
   }
   preference!!.flush()
 }

 flush() {
   if (preference === undefined) {
     this.getPreferences()
   }
   try {
     preference!!.flush()
   } catch (e) {
     Logger.error(this.TAG, 'Failed to flush, cause:' + e)
   }
 }

 private getPreferences() {
   try {

     let options: preferences.Options = { name:'myStore' };
     preference = preferences.getPreferencesSync(context, options);
   } catch (e) {
     Logger.error(this.TAG, 'Failed to get preferences, cause:' + e)
   }
 }

 private deletePreferences() {
   try {
     preference?.deleteSync('myStore')
   } catch (e) {
     Logger.error(this.TAG, 'Failed to delete preferences, cause:' + e)
   }
   preference = undefined
 }
}

export default new PreferencesUtil()

在UI中使用:

存储数据
Button('存储信息')
 .height(45)
 .margin({top:45})
 .onClick(()=>{
   PreferencesUtil.writeString("username","123456")
   this.showToast('已经存储')
 })
效果如图

#HarmonyOS NEXT体验官#  鸿蒙next 数据持久化 你不知道的事情_持久化_03




读取数据
Button('读取信息')
 .height(45)
 .margin({top:45})
 .onClick(()=>{
   let username=PreferencesUtil.readString("username",'')
   this.showToast('读取数据'+username)
 })

效果如图


#HarmonyOS NEXT体验官#  鸿蒙next 数据持久化 你不知道的事情_数据_04

完整代码:

import  PreferencesUtil  from './PreferencesUtil';
import prompt from '@ohos.promptAction';

@Entry
@Component
struct Index {
 @State message: string = 'Hello World';

 showToast(msg:string){
   prompt.showToast({
     message: msg
   })
 }
 build() {
    Column(){
      Button('存储信息')
        .height(45)
        .margin({top:45})
        .onClick(()=>{
          PreferencesUtil.writeString("username","123456")
          this.showToast('已经存储')
        })

      Button('读取信息')
        .height(45)
        .margin({top:45})
        .onClick(()=>{
          let username=PreferencesUtil.readString("username",'')
          this.showToast('读取数据'+username)
        })
    }
   .height('100%')
   .width('100%')
   .backgroundColor(Color.White)
 }
}

我们编写的PreferencesUtil 工具类来管理我们的数据持久化操作 我们不需要关心上下文 只需要传入我们的存储的key 和value即可 读取数据也是一样我们只需要传入我们的读取的key和传入默认值的key.

最后总结:

鸿蒙next里面的Preferences 和Android的里面的 Sharedpreferences 以及ios里面的 NSUserDefaults 比较类型是轻量级的数据存储 通常用来存储我们的账号密码用户token 等等信息。不建议存储大量数据, 大量数据建建议使用关系型数据库或者网络存储。那么今天的文章就讲到这里有更新的知识大家可以关注坚果派公众号我们持续更新。为了方便大家学习