Harmony状态管理AppStorageV2和PersistenceV2
前言
在HarmonyOS
应用开发过程中,我们已经学习过了不少关于状态管理相关的技术,如
@ObservedV2
装饰器和@Trace
装饰器:类属性变化观测@ComponentV2
装饰器:自定义组件@Local
装饰器:组件内部状态@Param
:组件外部输入@Once
:初始化同步一次@Event
装饰器:组件输出@Monitor
装饰器:状态变量修改监听@Provider
装饰器和@Consumer
装饰器:跨组件层级双向同步(没有讲过)@Computed
装饰器:计算属性@Type
装饰器:标记类属性的类型
以上状态管理技术,都是围绕着组件内部本身的。现在要讲解的AppStorageV2
和PersistenceV2
可以理解为应用/全局的状态管理技术。
AppStorageV2
是应用级别的数据管理技术,跨组件、跨页面。只要是主线程之内的UIAbility实例都可以共享数据。但是退出应用数
据会自动销毁。PersistenceV2
是应用级别的数据持久化技术,数据是直接存在设备磁盘上的,退出重新进入后,数据还存在
AppStorageV2
实际开发中,我们避免不了需要将数据实时共享在多个页面或者组件中,如个人信息,那么便可以考虑将数据存放在AppStorageV2
中。
AppStorageV2
是应用级别的数据管理技术,跨组件、跨页面。只要是主线程之内的UIAbility实例都可以共享数据。但是退出应用数据会自动
销毁。
AppStorageV2核心API
名称 | 作用 |
connect | 创建或获取储存的数据 |
remove | 删除指定key的储存数据 |
keys | 返回所有AppStorageV2中的key |
connect
创建或获取储存的数据
connect | 说明 |
参数 | type:指定的类型,若未指定key,则使用type的name作为key; keyOrDefaultCreater:指定的key,或者是默认数据的构造器; defaultCreator:默认数据的构造器。 |
返回值 | 创建或获取数据成功时,返回数据;否则返回undefined。 |
示例代码:
Index.ets
import { AppStorageV2, router } from '@kit.ArkUI' @ObservedV2 export class Person { @Trace age: number = 10 } @Entry @ComponentV2 struct Index { @Local person: Person = AppStorageV2.connect(Person, () => new Person)! build() { Column() { Text("A页面") // 预览器会失效 Button(`年龄:${this.person.age}`) .onClick(() => { this.person.age++ }) Button("跳转到B页面") .onClick(() => { router.pushUrl({ url: "pages/Index2" }) }) } } }
代码解释:
@Local person: Person = AppStorageV2.connect(Person, () => new Person)!
创建或者读取 key
为Person
的数据。
@Local
是用来修饰person,表示person是一个状态,状态改变时,会引起UI的更新
.connect(Person,..
Person
,作为connect
方法的第一个参数,表示 Person
类型
.connect(Person, () => new Person)!
,由于connect
中没有传入特定的key
,那么便将Person.name
视作key
。相当于
.connect(Person, Person.name,() => new Person)!
或者
.connect(Person,'Person',() => new Person)!
同时,因为有 () => new Person
的作用,那么初始的数据也有了。
最后,因为 connect
的返回值可能为空,因此在最后加上一个 !
表示非空断言
值得注意的是,以上代码在预览器上显示时,会得出 age = undefined
的结果,所以需要在模拟器上测试
最后,如果你在A页面使用以上代码,修改了 age,那么B页面上也使用了age。那么便可以看到两个页面的数据是一样的。
Index.ets
import { Person } from "./Index"; import { AppStorageV2 } from '@kit.ArkUI'; @Entry @ComponentV2 struct Index2 { @Local person: Person = AppStorageV2.connect(Person, () => new Person)! build() { Column() { Text("B页面") Button(`age:${this.person.age}`) } } }
结果
remove
删除指定key的储存数据
remove | 说明 |
参数 | keyOrType:需要删除的key;如果指定的是type类型,删除的key为type的name。 |
返回值 | 无。 |
比如:AppStorageV2.remove(Person)
其实是等价于
AppStorageV2.remove(Person.name)
或者 AppStorageV2.remove('Person')
示例代码:
import { AppStorageV2, router } from '@kit.ArkUI' @ObservedV2 export class Person { @Trace age: number = 10 } @Entry @ComponentV2 struct Index { @Local person: Person = AppStorageV2.connect(Person, () => new Person)! build() { Column({ space: 10 }) { Text("A页面") // 预览器会失效 Button(`年龄:${this.person.age}`) .onClick(() => { this.person.age++ }) Button("跳转到B页面") .onClick(() => { router.pushUrl({ url: "pages/Index2" }) }) Button("删除数据啦") .onClick(() => { AppStorageV2.remove(Person) }) } } }
如果此时执行了 remove
方法,那么此时无论A页面中如何修改 person
数据,AppStorageV2
中都不会跟随相应。B页面的perosn
数据都不会受到影响。
keys
返回所有AppStorageV2
中的key
示例代码
Button("返回AppStorageV2所有的keys") .onClick(() => { console.log("AppStorageV2.keys()", AppStorageV2.keys()) })
PersistenceV2
PersistenceV2
是应用级别的数据持久化技术,数据是直接存在设备磁盘上的,退出重新进入后,数据还存在
PersistenceV2
核心API和AppStorageV2
类似,都提供了 connect
、remove
和keys
。
import { PersistenceV2 } from '@kit.ArkUI' @ObservedV2 export class Person { @Trace age: number = 10 } @Entry @ComponentV2 struct Index { @Local person: Person = PersistenceV2.connect(Person, () => new Person)! build() { Column({ space: 10 }) { Text("A页面") // 预览器会失效 Button(`年龄:${this.person.age}`) .onClick(() => { this.person.age++ }) } } }
此时打开设备的文件目录,可以看到是实实在在把数据写到了磁盘中的。
/data/app/el2/100/base/com.example.你的包名/haps/entry/files/persistent_storage
需要注意的是,PersistenceV2
还有另外的两个API
save
由于非@Trace
的数据改变不会触发PersistenceV2
的自动持久化,因此可以手动的调用save
进行持久化notifyOnError
表示响应序列化或反序列化失败的回调 可以做错误处理
save
手动对@Trace的数据进行持久化
save | 说明 |
参数 | keyOrType:需要手动持久化的key;如果指定的是type类型,key为type的name。 |
返回值 | 无。 |
示例代码:
import { PersistenceV2 } from '@kit.ArkUI' export class Person { age: number = 10 } @Entry @ComponentV2 struct Index { @Local person: Person = PersistenceV2.connect(Person, () => new Person)! build() { Column({ space: 10 }) { Text("A页面") // 预览器会失效 Button(`年龄:${this.person.age}`) .onClick(() => { this.person.age++ console.log("this.person.age", this.person.age) PersistenceV2.save(Person) }) } } }
- 首次打开页面,直接修改数据,此时发现UI不会刷新,但是 日志输出却显示 age再增加(因为没有了@
ObservedV2
和@Trace
监听) - 此时退出应用,重新打开,却发现age会变成上一次增加后的数据。因为每次修改后,都调用了
save
方法。已经把数据持久化到磁盘中了。当重新打开页面时,会重新执行@Local person: Person = PersistenceV2.connect(Person, () => new Person)!
代码,便从磁盘中读取出数据渲染到UI上
notifyOnError
表示响应序列化或反序列化失败的回调 可以做错误处理
notifyOnError | 说明 |
参数 | callback:当序列化或者反序列化失败时,执行该回调;若传入undefined,取消该回调。 |
返回值 | 无。 |
示例代码:
// 接受序列化失败的回调 PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => { console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`); });
序列化的补充
如果要存储的数据是复杂类型嵌套的数据,需要使用 @Type
防止数据丢失
import { PersistenceV2, Type } from '@kit.ArkUI' class Son { weight: number = 100 } export class Person { age: number = 10 // 嵌套复杂类型,为了防止序列化数据失败,需要使用 @Type标记类型 @Type(Son) son: Son = new Son() }
小结
- 如果考虑需要全局共享数据,那么可以考虑使用
AppStorageV2
- 如果考虑数据需要持久化,那么可以使用
PersistenceV2
,因为是直接读写磁盘,所以不适合持久化大量数据,会导致应用性能下下降。