HarmonyOS第七章:应用状态共享(PersistentStorage、LocalStorage、AppStorage)

server/2024/9/25 4:56:21/

🎉 博客主页:【剑九_六千里-CSDN博客】【剑九_六千里-掘金社区】
🎨 上一篇文章:【HarmonyOS第六章:组件状态共享(父子组件传参、多层级组件传参、@Watch监听状态变化、@Observed与@ObjectLink、多层嵌套数据更新)】
🎠 系列专栏:【HarmonyOS系列】
💖 感谢大家点赞👍收藏⭐评论✍

在这里插入图片描述

在这里插入图片描述

文章目录

  • 1. PersistentStorage
    • 1.1. 简单数据类型的持久化,获取和修改
      • 1.1.1. 语法
      • 1.1.2. 使用示例
    • 1.2. 复杂数据类型的持久化,获取和修改
      • 1.2.1. 基本使用
      • 1.2.2. 使用 AppStorage.get() 获取数据,AppStorage.set()写入数据
      • 1.2.3. 使用 link 写入/获取数据
      • 1.3. 在其它页面获取存储的数据
  • 2. LocalStorage
    • 2.1. 基本使用
    • 2.2. 页面内共享
      • 2.2.1. 声明
      • 2.2.2. 接收
      • 2.2.3. 完整示例
    • 2.3. 不同页面间共享
    • 2.4. LocalStorage 存储的数据并不会持久化存储
  • 3. AppStorage
    • 3.1. 基础用法
    • 3.2. 组件共享
    • 3.3. set() 及 get() 方法
    • 3.4. AppStorage.link("user");
  • 4. 总结

1. PersistentStorage

PersistentStorage 允许将选定的 AppStorage 属性保留在设备磁盘上,以便在应用程序关闭后仍然保持持久化。以下是关于PersistentStorage 的一些细节和注意事项:

  • UI 和业务逻辑不应直接访问 PersistentStorage 中的属性,所有属性访问都应通过 AppStorage 进行。任何在 AppStorage 中的更改都会自动同步到 PersistentStorage
  • PersistentStorage 支持存储简单类型数据,如数字(number)、字符串(string)、布尔(boolean)、枚举(enum)等。
  • 如果需要存储对象类型的数据,可以将其转换为 JSON 字符串后再存储。
  • 注意,持久化变量最好是小于2KB的数据。如果开发者需要存储大量的数据,建议使用数据库API来进行管理和存储。

1.1. 简单数据类型的持久化,获取和修改

1.1.1. 语法

// 1. 通过 PersistentStorage.persistProp() 定义要存储的数据
// 参数一:存储的属性;参数二:存储的值
PersistentStorage.persistProp("xxx", xxx)// 2. 通过 @StorageProp() 或 @StorageLink() 接收数据
@StorageProp("xxx")
@StorageLink("xxx")// 3. 接收数据
xxx: number = 0;// 4. 通过 this.xxx 使用数据
this.xxx 

1.1.2. 使用示例

在这里插入图片描述

使用 PersistentStorage 存储数据,使用 @StorageLink@StorageLink 接收数据,同时数据支持修改:

// PersistentStorage 写入磁盘-app关闭数据依然存在,持久化存储
// 存储简单数据类型:number string boolean等等
// 存储负载数据类型:array object function
PersistentStorage.PersistProp("num",100)@Entry
@Component
struct Index {@StorageLink("num") // 获取数据,单向数据流,只是组件内可以改变数据,并不会写入 PersistentStorage// @StorageLink("num") // 获取数据,双向数据流,本地修改会同步写入到 PersistentStoragenum:number=0 // 如果未存储,默认初始值build() {Column({space:10}) {Text(`${this.num}`)Button("修改age").onClick(()=>{this.num++})}.width('100%').height('100%')}
}

1.2. 复杂数据类型的持久化,获取和修改

1.2.1. 基本使用

在这里插入图片描述

  • 注意:存储复杂类型数据时,value 必须是json
// 注意:存储复杂类型数据时,value 必须是json
PersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct Index {@StorageLink("user") // 获取数据 @StorageLink("user") 或 @StorageProp("user")userInfo: string = "{}" // 默认值@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectbuild() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;})}.width("100%").height("100%")}
}

1.2.2. 使用 AppStorage.get() 获取数据,AppStorage.set()写入数据

这个例子中使用 @StorageLink("user") 获取数据,可以实现需求,同时我们也可以使用 AppStorage.get() 来获取数据:
在这里插入图片描述

// 注意:存储复杂类型数据时,value 必须是json
PersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct Index {@StorageLink("user")userInfo: string = "{}"@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectbuild() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;})Button("获取user").onClick(() => {const res = AppStorage.get<string>("user") // 获取到的是原数据console.log(res)})}.width("100%").height("100%")}
}

此时点击修改Age按钮,可以发现age被改变了,但 AppStorage.get() 获取到的是改变前的数据,此时可以通过 AppStorage.set() 重新设置数据 :
在这里插入图片描述

// 注意:存储复杂类型数据时,value 必须是json
PersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct Index {@StorageLink("user")userInfo: string = "{}"@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectbuild() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;AppStorage.set<string>("user", JSON.stringify(this.user)); // 将修改后的数据重新存储})Button("获取user").onClick(() => {const res = AppStorage.get<string>("user")console.log(res)})}.width("100%").height("100%")}
}

1.2.3. 使用 link 写入/获取数据

在这里插入图片描述

// 注意:存储复杂类型数据时,value 必须是jsonPersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct Index {@StorageLink("user")@Watch("update")userInfo: string = "{}"@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectupdate() {this.user = JSON.parse(this.userInfo)}build() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;// 将修改后的数据重新存储AppStorage.set<string>("user", JSON.stringify(this.user));})Button("AppStorage.get获取user").onClick(() => {// 获取数据const res = AppStorage.get<string>("user")console.log(res)})Button("Link 方式 获取/写入user").onClick(() => {// 获取数据const userLink: SubscribedAbstractProperty<string> = AppStorage.link("user")console.log(userLink.get())userLink.set(JSON.stringify({name: "李四", age: 21}))})}.width("100%").height("100%")}
}

1.3. 在其它页面获取存储的数据

测试步骤如下:

  • 修改主页面 age;
  • 点击跳转页面,跳转到其它页面;
  • 查看其它页面获取的数据是否正确;
  • 修改其它页面的数据;
  • 返回主页面,查看主页面数据是否同步更新;

通过 router 跳转页面:

// 注意:存储复杂类型数据时,value 必须是json
import router from '@ohos.router'; // 导入路由模块PersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct PersistentStorage3 {@StorageLink("user")@Watch("update")userInfo: string = "{}"@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectupdate() {this.user = JSON.parse(this.userInfo)}build() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;// 将修改后的数据重新存储AppStorage.set<string>("user", JSON.stringify(this.user));})Button("AppStorage.get获取user").onClick(() => {// 获取数据const res = AppStorage.get<string>("user")console.log(res)})Button("Link 方式 获取/写入user").onClick(() => {// 获取数据const userLink: SubscribedAbstractProperty<string> = AppStorage.link("user")console.log(userLink.get())userLink.set(JSON.stringify({name: "李四", age: 21}))})Button("跳转页面").onClick(() => {// 路由跳转router.pushUrl({url: "pages/PersistentStorage4"})})}.width("100%").height("100%")}
}

路由跳转要求页面必须在 src/main/resources/base/profile/main_pages.json 中进行配置:在这里插入图片描述

PersistentStorage3 页面初始数据为 18 :

在这里插入图片描述

点击 PersistentStorage3 修改Age,并点击跳转到 PersistentStorage4 页面,可以看到 PersistentStorage4 页面获取到了更新后的数据:

在这里插入图片描述
PersistentStorage4 页面修改Age:
在这里插入图片描述

返回 PersistentStorage3 页面,可以看到数据也更新了:
在这里插入图片描述
在这里插入图片描述
因此,使用 PersistentStorage.persistProp() 存储的数据,在所有页面中都可以使用并修改。

2. LocalStorage

LocalStorage 是用于页面级的UI状态存储,通过 @Entry 装饰器接收的参数可以在同一页面内共享相同的LocalStorage 实例。此外,LocalStorage 也可以在 UIAbility 内,在不同页面之间共享状态数据。

2.1. 基本使用

2.2. 页面内共享

在这里插入图片描述

2.2.1. 声明

使用 const storage = new LocalStorage({ key: value }) 来创建LocalStorage实例,其中 key 是存储的键名,value 是初始值。

interface User {name: string;age: number;
}const user: User = { name: '张三', age: 18 };
const storage = new LocalStorage(user);

2.2.2. 接收

  • @Entry 构造器接收一个参数,该参数是通过 const storage = new LocalStorage(user); 存储的数据
@Entry(storage)
  • 单向数据绑定:使用 @LocalStorageProp('user') 装饰器,可以在组件内对LocalStorage的值进行单向修改,仅在组件内生效。
  • 双向数据绑定:使用 @LocalStorageLink('user') 装饰器,可以实现全局范围内对LocalStorage的值进行双向修改,多个组件可以共享相同的LocalStorage实例。
// @LocalStorageProp("user")
@LocalStorageLink("user")

2.2.3. 完整示例

// 非持久化,只能在一个 UIAbility 中使用
interface User {name: string;age: number;
}const user: User = { name: '张三', age: 18 };
const storage = new LocalStorage(user);@Entry(storage)
@Component
struct Index {// @LocalStorageProp("user") // 使用@LocalStorageProp("user")时,修改值,子组件不会同步@LocalStorageLink("user") // 使用@LocalStorageLink("user")时,修改值,子组件会同步userInfo: User = { name: "李四", age: 18 } // 默认值build() {Column() {Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("Index修改age").onClick(() => {this.userInfo.age++;})ChildA()ChildB()}.width('100%')}
}@Component
struct ChildA {@LocalStorageProp("user")userInfo: User = { name: "Tom", age: 20 }build() {Column({ space: 10 }) {Text(`${this.userInfo.name}---${this.userInfo.age}`).fontColor(Color.White)Button("ChildA修改age").onClick(() => {// @LocalStorageProp("user") 接收的参数,子组件修改时,父组件不会同步this.userInfo.age++})}.width('100%').height(100).backgroundColor(Color.Blue)}
}@Component
struct ChildB {@LocalStorageLink("user")userInfo: User = { name: "Tom", age: 20 }build() {Column({ space: 10 }) {Text(`${this.userInfo.name}---${this.userInfo.age}`).fontColor(Color.White)Button("ChildB修改age").onClick(() => {// @LocalStorageLink("user") 接收的参数,子组件修改时,父组件会同步this.userInfo.age++})}.width('100%').height(100).backgroundColor(Color.Brown)}
}

2.3. 不同页面间共享

  • 创建一个 IndexOther.ets 文件
    在这里插入图片描述

  • IndexOther.ets 加入到路由中
    在这里插入图片描述

  • UIAbility中创建LocalStorage:可以在UIAbility中创建LocalStorage实例,并通过 loadContent 方法提供给加载的窗口,以便在页面间共享状态数据。
    在这里插入图片描述

  • 在页面中获取LocalStorage实例:在页面中,可以通过 const storage = LocalStorage.getShared() 来获取LocalStorage的实例。然后,可以通过 @Entry(storage) 将实例传递给页面,以便在页面中使用LocalStorage来共享和管理状态数据。

// Index.etsimport router from "@ohos.router";
interface User {name: string;age: number;
}
const storage = LocalStorage.getShared();@Entry(storage)
@Component
struct Index {@LocalStorageLink("user")userInfo: User = { name: "李四", age: 18 } // 默认值build() {Column() {Text("这是Index页面")Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("Index修改age").onClick(() => {this.userInfo.age++;})Button("跳转到IndexOther页面").onClick(() => {router.pushUrl({url: "pages/IndexOther"})})}.width('100%')}
}// IndexOther.ets
import router from "@ohos.router";// 非持久化,只能在一个 UIAbility 中使用
interface UserInfo {name: string;age: number;
}const storage = LocalStorage.getShared();@Entry(storage)
@Component
struct IndexOther {@LocalStorageLink("user")userInfo: UserInfo = { name: "李四", age: 18 } // 默认值build() {Column() {Text("这是IndexOther页面")Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("Index修改age").onClick(() => {this.userInfo.age++;})Button("跳转到Index页面").onClick(() => {router.pushUrl({url: "pages/Index"})})}.width('100%')}
}

此时不论在哪一个页面修改数据,都会同步:
在这里插入图片描述

2.4. LocalStorage 存储的数据并不会持久化存储

打开模拟机:

在这里插入图片描述
在这里插入图片描述
初始值如下:

在这里插入图片描述
修改数据:

在这里插入图片描述
关闭当前页面:

在这里插入图片描述
再次打开后发现数据已经变成了初始值,因此 LocalStorage 存储的数据并不会持久化存储:

在这里插入图片描述

3. AppStorage

AppStorage 是应用程序级别的全局UI状态存储,与应用程序的进程绑定。它由UI框架在应用程序启动时创建,用于中央存储应用程序的UI状态属性。

3.1. 基础用法

  • 初始化使用:可以使用 AppStorage.SetOrCreate(key, value) 来初始化AppStorage中的数据,其中 key 是存储的键名,value 是初始值。
  • 单向数据绑定:使用 @StorageProp('user') 装饰器,可以在组件内对AppStorage的值进行单向修改,仅在组件内生效。
  • 双向数据绑定:使用 @StorageLink('user') 装饰器,可以实现全局范围内对AppStorage的值进行双向修改,多个组件可以共享相同的AppStorage实例,从而实现全局状态管理。
interface Person {name: string;age: number;
}const user: Person = { name: "张三", age: 18 }
AppStorage.setOrCreate<Person>("user", user)@Entry
@Component
struct Index {// @StorageProp("user")@StorageLink("user")userInfo: Person = { name: "李四", age: 28 } // 默认值build() {Column() {Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("修改age").onClick(() => {this.userInfo.age++;})}.width("100%").height("100%")}
}

3.2. 组件共享

在这里插入图片描述
子组件中可共享源数据:

interface Person {name: string;age: number;
}const user: Person = { name: "张三", age: 18 }
AppStorage.setOrCreate<Person>("user", user)@Entry
@Component
struct Index {// @StorageProp("user")@StorageLink("user")userInfo: Person = { name: "李四", age: 28 } // 默认值build() {Column() {Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("直接修改age").onClick(() => {this.userInfo.age++;})ChildA()}.width("100%").height("100%")}
}@Component
struct ChildA {@StorageProp("user") // 不会同步// @StorageLink("user") // 会同步user: Person = { name: "Tom", age: 20 }build() {Column({ space: 10 }) {Text(this.user.name + "----" + this.user.age).fontColor(Color.White)Button("ChildA修改age").onClick(() => {this.user.age++})}.width('100%').height(100).backgroundColor(Color.Blue)}
}

3.3. set() 及 get() 方法

  • 使用 AppStorage.set<ValueType>(key, value) 可以覆盖AppStorage中的数据,其中 key 是存储的键名,value 是要存储的新值。
  • 使用 AppStorage.get<ValueType>(key) 可以获取AppStorage中存储的数据,其中 ValueType 是数据的类型,key 是存储的键名。
    在这里插入图片描述
interface Person {name: string;age: number;
}const user: Person = { name: "张三", age: 18 }
AppStorage.setOrCreate<Person>("user", user)@Entry
@Component
struct Index {// @StorageProp("user")@StorageLink("user")userInfo: Person = { name: "李四", age: 28 } // 默认值build() {Column() {Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("直接修改age").onClick(() => {this.userInfo.age++;})Button("通过set修改age").onClick(() => {AppStorage.set<Person>("user", { name: "李四", age: 28 })})Button("通过get获取user").onClick(() => {const user = AppStorage.get<Person>("user")console.log(`${user?.name}---${user?.age}`)})}.width("100%").height("100%")}
}

3.4. AppStorage.link(“user”);

  • 使用 const link: SubscribedAbstractProperty<ValueType> = AppStorage.Link(key) 可以创建一个与AppStorage中数据关联的链接,通过这个链接可以修改和获取数据。您可以使用 link.set(value) 来修改数据,使用 link.get() 来获取数据。这个链接允许您在不同组件之间共享数据,并保持数据的同步。

在这里插入图片描述

4. 总结

  • LocalStorage– 一个UIAbility状态(内存–非持久化–非全应用
  • AppStorage– 应用内状态–多UIAbility共享(内存–非持久化-退出App应用 数据消失
  • PersistenStroage–全局持久化状态(写入磁盘–持久化状态-退出App应用 数据不消失

http://www.ppmy.cn/server/121668.html

相关文章

Java-数据结构-排序(三) |ू・ω・` )

目录 ❄️一、归并排序&#xff1a; ☞ 基本思想&#xff1a; ☞ 代码&#xff1a; ☞ 归并排序的非递归方法&#xff1a; ❄️二、排序算法的分析&#xff1a; ❄️三、非基于比较的排序&#xff1a; ❄️总结&#xff1a; ❄️一、归并排序&#xff1a; ☞ 基本思想&#xf…

深度学习:数据增强

目录 前言 一、为什么要使用数据增强&#xff1f; 二、数据增强有哪些方法&#xff1f; 1. 几何变换 2. 颜色变换 3. 噪声添加 4. 裁剪 5. 混合技术 6. 其他方法 三、代码实现 前言 数据增强是深度学习中常用的一种技术&#xff0c;旨在通过对训练数据进行各种变换来…

研1日记15

1. 文心一言生成&#xff1a; 在PyTorch中&#xff0c;nn.AdaptiveAvgPool1d(1)是一个一维自适应平均池化层。这个层的作用是将输入的特征图&#xff08;或称为张量&#xff09;在一维上进行自适应平均池化&#xff0c;使得输出特征图的大小在指定的维度上变为1。这意味着&…

《微信小程序实战(4) · 地图导航功能》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

24年蓝桥杯及攻防世界赛题-MISC-1

2 What-is-this AZADI TOWER 3 Avatar 题目 一个恐怖份子上传了这张照片到社交网络。里面藏了什么信息?隐藏内容即flag 解题 ┌──(holyeyes㉿kali2023)-[~/Misc/tool-misc/outguess] └─$ outguess -r 035bfaa85410429495786d8ea6ecd296.jpg flag1.txt Reading 035bf…

Redis常见知识点

数据类型 String Redis字符串存储字节序列&#xff0c;包括文本、序列化对象和二进制数组。 默认情况下 单个Redis字符串最大值不能超过512m 常用命令 SETNX仅当键不存在时才存储字符串值。对于实现锁很有用。MGET在一次操作中检索多个字符串值。INCRBY原子地增加&#xff…

详解 C++中的模板

目录 前言 一、函数模板 1.定义 2.函数模板的实现 3.模板函数的实例化 4.模板参数的省略 1.函数模板的实参推导 2.类模板的实参推导 3.默认模板参数 4.特殊情况:无法推导的模板 5.推导失败的情况 二、类模板 1.概念和定义 2.类模板定义 3.类模板的使用 4.类模板…

23个Python在自然语言处理中的应用实例

在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;Python作为一门功能强大的编程语言&#xff0c;凭借其丰富的库和工具集&#xff0c;成为了实现各种NLP任务的首选。以下是一个关于Python在NLP中应用的广泛实例的前言&#xff0c;旨在概述Python在NLP领域的多样性和…