1# PersistenceV2: 持久化储存UI状态
2
3为了增强状态管理框架对持久化存储UI的能力,开发者可以使用PersistenceV2存储持久化的数据。AppStorageV2是运行时内存,但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistenceV2。
4
5PersistenceV2是应用程序中的可选单例对象。此对象的作用是持久化存储UI相关的数据,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。
6
7PersistenceV2提供状态变量持久化能力,开发者可以通过connect绑定同一个key,在状态变量变换和应用冷启动时,实现持久化能力。
8
9在阅读本文当前,建议提前阅读:[\@ComponentV2](./arkts-new-componentV2.md),[\@ObservedV2和\@Trace](./arkts-new-observedV2-and-trace.md),配合阅读:[PersistentV2-API文档](../reference/apis-arkui/js-apis-StateManagement.md#persistencev2)
10
11>**说明:**
12>
13>PersistenceV2从API version 12开始支持。
14>
15
16
17## 概述
18
19PersistenceV2是在应用UI启动时会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。数据通过唯一的键字符串值访问。不同于AppStorageV2,PersistenceV2还将最新数据储存在设备磁盘上(持久化)。这意味着,应用退出再次启动后,依然能保存选定的结果。
20
21对于与PersistenceV2关联的[\@ObservedV2](arkts-new-observedV2-and-trace.md)对象,该对象的[\@Trace](arkts-new-observedV2-and-trace.md)属性的变化,会触发**整个关联对象的自动持久化**;非[\@Trace](arkts-new-observedV2-and-trace.md)属性的变化则不会,如有必要,可调用PersistenceV2 API手动持久化。
22
23PersistenceV2可以和UI组件同步,且可以在应用业务逻辑中被访问。
24
25PersistenceV2支持应用的[主线程](../application-models/thread-model-stage.md)内多个UIAbility实例间的状态共享。
26
27
28## 使用说明
29
30### connect:创建或获取储存的数据
31
32```JavaScript
33static connect<T extends object>(
34    type: TypeConstructorWithArgs<T>,
35    keyOrDefaultCreator?: string | StorageDefaultCreator<T>,
36    defaultCreator?: StorageDefaultCreator<T>
37): T | undefined;
38```
39
40| connect      | 说明                                                  |
41| ------------ | ----------------------------------------------------- |
42| 参数         | type:指定的类型,若未指定key,则使用type的name作为key;</br > keyOrDefaultCreater:指定的key,或者是默认数据的构造器;</br > defaultCreator:默认数据的构造器。                                          |
43| 返回值       | 创建或获取数据成功时,返回数据;否则返回undefined。 |
44
45>**说明:**
46>
47>1、若未指定key,使用第二个参数作为默认构造器;否则使用第三个参数作为默认构造器(第二个参数非法也使用第三个参数作为默认构造器);
48>
49>2、确保数据已经存储在PersistenceV2中,可省略默认构造器,获取存储的数据;否则必须指定默认构造器,不指定将导致应用异常;
50>
51>3、同一个key,connect不同类型的数据会导致应用异常,应用需要确保类型匹配;
52>
53>4、key建议使用有意义的值,可由字母、数字、下划线组成,长度不超过255,使用非法字符或空字符的行为是未定义的;
54>
55>5、关联[\@Observed](arkts-observed-and-objectlink.md)对象时,由于该类型的name属性未定义,需要指定key或者自定义name属性。
56
57### remove:删除指定key的储存数据
58
59```JavaScript
60static remove<T>(keyOrType: string | TypeConstructorWithArgs<T>): void;
61```
62
63| remove       | 说明                                                  |
64| ------------ | ----------------------------------------------------- |
65| 参数         | keyOrType:需要删除的key;如果指定的是type类型,删除的key为type的name。                                          |
66| 返回值       | 无。 |
67
68>**说明:**
69>
70>删除PersistenceV2中不存在的key会报警告。
71
72### keys:返回所有PersistenceV2中的key
73
74```JavaScript
75static keys(): Array<string>;
76```
77
78| keys         | 说明                                                  |
79| ------------ | ----------------------------------------------------- |
80| 参数         | 无。                                         |
81| 返回值       | 所有PersistenceV2中的key。 |
82
83
84### save:手动持久化数据
85
86```JavaScript
87static save<T>(keyOrType: string | TypeConstructorWithArgs<T>): void;
88```
89
90| save         | 说明                                                  |
91| ------------ | ----------------------------------------------------- |
92| 参数         | keyOrType:需要手动持久化的key;如果指定的是type类型,key为type的name。                                          |
93| 返回值       | 无。 |
94
95>**说明:**
96>
97>由于非[\@Trace](arkts-new-observedV2-and-trace.md)的数据改变不会触发PersistenceV2的自动持久化,如有必要,可调用该接口持久化对应key的数据;
98>
99>手动持久化当前内存中不处于connect状态的key是无意义的。
100
101
102### notifyOnError:响应序列化或反序列化失败的回调
103
104```JavaScript
105static notifyOnError(callback: PersistenceErrorCallback | undefined): void;
106```
107
108| notifyOnError| 说明                                                  |
109| ------------ | ----------------------------------------------------- |
110| 参数         | callback:当序列化或者反序列化失败时,执行该回调;若传入undefined,取消该回调。|
111| 返回值       | 无。 |
112
113>**说明:**
114>
115>将数据存入磁盘时,需要对数据进行序列化;当某个key序列化失败时,错误是不可预知的;可调用该接口捕获异常。
116
117
118## 使用限制
119
1201、需要配合UI使用(UI线程),不能在其他线程使用,如不支持@Sendable;
121
1222、不支持collections.Setcollections.Map等类型;
123
1243、不支持非buildin类型,如PixelMap、NativePointer、ArrayList等Native类型;
125
1264、单个key支持数据大小约8k,过大会导致持久化失败;
127
1285、持久化的数据必须是class对象,不能是容器(如Array、Set、Map),不能是buildin的构造对象(如Date、Number);
129
1306、不支持循环引用的对象;
131
1327、只有[\@Trace](arkts-new-observedV2-and-trace.md)的数据改变会触发自动持久化,如V1状态变量、[\@Observed](arkts-observed-and-objectlink.md)对象、普通数据的改变不会触发持久化;
133
1348、不宜大量持久化数据,可能会导致页面卡顿。
135
136## 使用场景
137
138### 在两个页面之间存储数据
139
140页面1
141```ts
142import { PersistenceV2 } from '@kit.ArkUI';
143import { Sample } from '../Sample';
144
145// 接受序列化失败的回调
146PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
147  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
148});
149
150@Entry
151@ComponentV2
152struct Page1 {
153  // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
154  // 对于需要换connect对象的prop属性,需要加@Local修饰(不建议对属性换connect的对象)
155  @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!;
156  pageStack: NavPathStack = new NavPathStack();
157
158  build() {
159    Navigation(this.pageStack) {
160      Column() {
161        Button('Go to page2')
162          .onClick(() => {
163            this.pageStack.pushPathByName('Page2', null);
164          })
165
166        Button('Page1 connect the key Sample')
167          .onClick(() => {
168            // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
169            // 不建议对prop属性换connect的对象
170            this.prop = PersistenceV2.connect(Sample, 'Sample', () => new Sample())!;
171          })
172
173        Button('Page1 remove the key Sample')
174          .onClick(() => {
175            // 从PersistenceV2中删除后,prop将不会再与key为Sample的值关联
176            PersistenceV2.remove(Sample);
177          })
178
179        Button('Page1 save the key Sample')
180          .onClick(() => {
181            // 如果处于connect状态,持久化key为Sample的键值对
182            PersistenceV2.save(Sample);
183          })
184
185        Text(`Page1 add 1 to prop.p1: ${this.prop.f.p1}`)
186          .fontSize(30)
187          .onClick(() => {
188            this.prop.f.p1++;
189          })
190
191        Text(`Page1 add 1 to prop.p2: ${this.prop.f.p2}`)
192          .fontSize(30)
193          .onClick(() => {
194            // 页面不刷新,但是p2的值改变了
195            this.prop.f.p2++;
196          })
197
198        // 获取当前PersistenceV2里面的所有key
199        Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`)
200          .fontSize(30)
201      }
202      }
203  }
204}
205```
206
207页面2
208```ts
209import { PersistenceV2 } from '@kit.ArkUI';
210import { Sample } from '../Sample';
211
212@Builder
213export function Page2Builder() {
214  Page2()
215}
216
217@ComponentV2
218struct Page2 {
219  // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
220  // 对于需要换connect对象的prop属性,需要加@Local修饰(不建议对属性换connect的对象)
221  @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!;
222  pathStack: NavPathStack = new NavPathStack();
223
224  build() {
225    NavDestination() {
226      Column() {
227        Button('Page2 connect the key Sample1')
228          .onClick(() => {
229            // 在PersistenceV2中创建一个key为Sample1的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
230            // 不建议对prop属性换connect的对象
231            this.prop = PersistenceV2.connect(Sample, 'Sample1', () => new Sample())!;
232          })
233
234        Text(`Page2 add 1 to prop.p1: ${this.prop.f.p1}`)
235          .fontSize(30)
236          .onClick(() => {
237            this.prop.f.p1++;
238          })
239
240        Text(`Page2 add 1 to prop.p2: ${this.prop.f.p2}`)
241          .fontSize(30)
242          .onClick(() => {
243            // 页面不刷新,但是p2的值改变了;只有重新初始化才会改变
244            this.prop.f.p2++;
245          })
246
247        // 获取当前PersistenceV2里面的所有key
248        Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`)
249          .fontSize(30)
250      }
251    }
252    .onReady((context: NavDestinationContext) => {
253      this.pathStack = context.pathStack;
254    })
255  }
256}
257```
258使用Navigation时,需要添加配置系统路由表文件src/main/resources/base/profile/route_map.json,并替换pageSourceFile为Page2页面的路径,并且在module.json5中添加:"routerMap": "$profile:route_map"。
259```json
260{
261  "routerMap": [
262    {
263      "name": "Page2",
264      "pageSourceFile": "src/main/ets/pages/Page2.ets",
265      "buildFunction": "Page2Builder",
266      "data": {
267        "description" : "AppStorageV2 example"
268      }
269    }
270  ]
271}
272```
273
274数据页面
275```ts
276import { Type } from '@kit.ArkUI';
277
278// 数据中心
279@ObservedV2
280class SampleChild {
281  @Trace p1: number = 0;
282  p2: number = 10;
283}
284
285@ObservedV2
286export class Sample {
287  // 对于复杂对象需要@Type修饰,确保序列化成功
288  @Type(SampleChild)
289  @Trace f: SampleChild = new SampleChild();
290}
291```
292
293