• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# PersistenceV2: 持久化储存UI状态
2
3为了增强状态管理框架对持久化存储UI的能力,开发者可以使用PersistenceV2存储持久化的数据。
4
5PersistenceV2是应用程序中的可选单例对象。此对象的作用是持久化存储UI相关的数据,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。
6
7PersistenceV2提供状态变量持久化能力,开发者可以通过connect或者globalConnect绑定同一个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>globalConnect从API version 18开始支持,行为和connect保持一致,唯一的区别为connect的底层存储路径为module级别的路径,而globalConnect的底层存储路径为应用级别,详细区别见使用场景[在不同的module中使用connect和globalConnect](#在不同的module中使用connect和globalconnect)。
16
17
18## 概述
19
20PersistenceV2是在应用UI启动时会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。数据通过唯一的键字符串值访问。不同于AppStorageV2,PersistenceV2还将最新数据储存在设备磁盘上(持久化)。这意味着,应用退出再次启动后,依然能保存选定的结果。
21
22对于与PersistenceV2关联的[\@ObservedV2](./arkts-new-observedV2-and-trace.md)对象,该对象的[\@Trace](./arkts-new-observedV2-and-trace.md)属性的变化,会触发**整个关联对象的自动持久化**;非[\@Trace](./arkts-new-observedV2-and-trace.md)属性的变化则不会,如有必要,可调用PersistenceV2 API手动持久化。
23
24PersistenceV2可以和UI组件同步,且可以在应用业务逻辑中被访问。
25
26PersistenceV2支持应用的[主线程](../../application-models/thread-model-stage.md)内多个UIAbility实例间的状态共享。
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 > keyOrDefaultCreator:指定的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>6、数据存储路径为module级别,即哪个module调用了connect,数据副本存入对应module的持久化文件中。如果多个module使用相同的key,则数据为最先使用connect的module,并且PersistenceV2中的数据也会存入最先使用connect的module里。
58>
59>7、因为存储路径在应用第一个ability启动时就已确定,为该ability所属的module。如果一个ability调用了connect,并且该ability能被不同module的拉起, 那么ability存在多少种启动方式,就会有多少份数据副本。
60
61### globalConnect:创建或获取储存的数据
62
63```ts
64// globalConnect 接口
65static globalConnect<T extends object>(
66    type: ConnectOptions<T>
67  ): T | undefined;
68```
69
70```ts
71// ConnectOptions参数
72class ConnectOptions<T extends object> {
73  type: TypeConstructorWithArgs<T>;	// 必选,指定的类型;
74  key?: string;	// 可选,传入的key,若未指定key,则使用type的name作为key;
75  defaultCreator?: StorageDefaultCreator<T>;	// 默认数据的构造器,建议填写;
76  areaMode?: contextConstant.AreaMode;	// 可选,加密参数;
77}
78```
79
80| globalConnect | 说明                                                      |
81| ------------- | --------------------------------------------------------- |
82| 参数          | type:传入的connect参数,详细说明见ConnectOptions参数说明。 |
83| 返回值        | 创建或获取数据成功时,返回数据;否则返回undefined。       |
84
85| ConnectOptions参数 | 说明                                                         |
86| :----------------: | :----------------------------------------------------------- |
87|        type        | TypeConstructorWithArgs\<T\>,必选参数,指定的类型。         |
88|        key         | string,传入的key,不传则使用type的名字作为key。             |
89|   defaultCreator   | StorageDefaultCreator\<T\>,默认数据的构造器,建议传递,如果globalConnect是第一次连接key,不传会报错。 |
90|      areaMode      | contextConstant.AreaMode,加密级别:EL1-EL5,详见[加密级别](../../application-models/application-context-stage.md#获取和修改加密分区),对应数值:0-4,不传时默认为EL2,不同加密级别对应不同的加密分区,即不同的存储路径,传入的加密等级数值不在0-4会直接运行crash。 |
91
92> **说明:**
93>
94> 1、若未指定key,使用第二个参数作为默认构造器;否则使用第三个参数作为默认构造器(第二个参数非法也使用第三个参数作为默认构造器)。
95>
96> 2、确保数据已经存储在PersistenceV2中,可省略默认构造器,获取存储的数据;否则必须指定默认构造器,不指定将导致应用异常。
97>
98> 3、同一个key,globalConnect不同类型的数据会导致应用异常,应用需要确保类型匹配。
99>
100> 4、key建议使用有意义的值,可由字母、数字、下划线组成,长度不超过255,使用非法字符或空字符的行为是未定义的。
101>
102> 5、关联[\@Observed](./arkts-observed-and-objectlink.md)对象时,因为该类型的name属性未定义,需要指定key或者自定义name属性。
103>
104> 6、数据的存储为应用级别的路径,不同module使用相同的key和相同的加密分区进行globalConnect,存储的数据副本应用仅有一份。
105>
106> 7、globalConnect使用同一个key但设置了不同的加密级别,数据为第一个使用globalConnect的加密级别,并且PersistenceV2中的数据也会存入最先使用key的加密级别。
107>
108> 8、connect和globalConnect不建议混用,因为数据副本路径不同,如果混用,则key不可以一样,否则会crash。
109>
110> 9、EL5加密要想生效,需要开发者在module.json中配置字段ohos.permission.PROTECT_SCREEN_LOCK_DATA,使用说明见[声明权限](../../security/AccessToken/declare-permissions.md)。
111
112### remove:删除指定key的储存数据
113
114```ts
115static remove<T>(keyOrType: string | TypeConstructorWithArgs<T>): void;
116```
117
118| remove       | 说明                                                  |
119| ------------ | ----------------------------------------------------- |
120| 参数         | keyOrType:需要删除的key;如果指定的是type类型,删除的key为type的name。                                          |
121| 返回值       | 无。 |
122
123>**说明:**
124>
125>删除PersistenceV2中不存在的key会报警告。
126
127### keys:返回所有PersistenceV2中的key
128
129```ts
130static keys(): Array<string>;
131```
132
133| keys         | 说明                                                  |
134| ------------ | ----------------------------------------------------- |
135| 参数         | 无。                                         |
136| 返回值       | 所有PersistenceV2中的key。 |
137
138> **说明:**
139>
140> keys会从返回module级别存储路径和应用级别存储路径中的所有key。
141
142### save:手动持久化数据
143
144```ts
145static save<T>(keyOrType: string | TypeConstructorWithArgs<T>): void;
146```
147
148| save         | 说明                                                  |
149| ------------ | ----------------------------------------------------- |
150| 参数         | keyOrType:需要手动持久化的key;如果指定的是type类型,key为type的name。                                          |
151| 返回值       | 无。 |
152
153>**说明:**
154>
155>由于非[\@Trace](./arkts-new-observedV2-and-trace.md)的数据改变不会触发PersistenceV2的自动持久化,如有必要,可调用该接口持久化对应key的数据。
156>
157>手动持久化当前内存中不处于connect状态的key是无意义的。
158
159
160### notifyOnError:响应序列化或反序列化失败的回调
161
162```ts
163static notifyOnError(callback: PersistenceErrorCallback | undefined): void;
164```
165
166| notifyOnError| 说明                                                  |
167| ------------ | ----------------------------------------------------- |
168| 参数         | callback:当序列化或者反序列化失败时,执行该回调;若传入undefined,取消该回调。|
169| 返回值       | 无。 |
170
171>**说明:**
172>
173>将数据存入磁盘时,需要对数据进行序列化;当某个key序列化失败时,错误是不可预知的;可调用该接口捕获异常。
174
175
176## 使用限制
177
1781、需要配合UI使用(UI线程),不能在其他线程使用,如不支持@Sendable。
179
1802、不支持collections.Setcollections.Map等类型。
181
1823、不支持非buildin类型,如PixelMap、NativePointer、ArrayList等Native类型。
183
1844、单个key支持数据大小约8k,过大会导致持久化失败。
185
1865、持久化的数据必须是class对象,不能是容器(如Array、Set、Map),不能是buildin的构造对象(如Date、Number)。
187
1886、不支持循环引用的对象。
189
1907、只有[\@Trace](./arkts-new-observedV2-and-trace.md)的数据改变会触发自动持久化,如V1状态变量、[\@Observed](./arkts-observed-and-objectlink.md)对象、普通数据的改变不会触发持久化。
191
1928、不宜大量持久化数据,可能会导致页面卡顿。
193
1949、connect和globalConnect不建议混用,如果混用,key不能一样,否则应用crash。
195
19610、不支持存储基本类型,如string、number、boolean等。
197
198## 使用场景
199
200### 在两个页面之间存储数据
201
202数据页面
203```ts
204// Sample.ets
205import { Type } from '@kit.ArkUI';
206
207// 数据中心
208@ObservedV2
209class SampleChild {
210  @Trace p1: number = 0;
211  p2: number = 10;
212}
213
214@ObservedV2
215export class Sample {
216  // 对于复杂对象需要@Type修饰,确保序列化成功
217  @Type(SampleChild)
218  @Trace f: SampleChild = new SampleChild();
219}
220```
221
222页面1
223```ts
224// Page1.ets
225import { PersistenceV2 } from '@kit.ArkUI';
226import { Sample } from '../Sample';
227
228// 接受序列化失败的回调
229PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
230  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
231});
232
233@Entry
234@ComponentV2
235struct Page1 {
236  // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
237  // 对于需要换connect对象的prop属性,需要加@Local修饰(不建议对属性换connect的对象)
238  @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!;
239  pageStack: NavPathStack = new NavPathStack();
240
241  build() {
242    Navigation(this.pageStack) {
243      Column() {
244        Button('Go to page2')
245          .onClick(() => {
246            this.pageStack.pushPathByName('Page2', null);
247          })
248
249        Button('Page1 connect the key Sample')
250          .onClick(() => {
251            // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
252            // 不建议对prop属性换connect的对象
253            this.prop = PersistenceV2.connect(Sample, 'Sample', () => new Sample())!;
254          })
255
256        Button('Page1 remove the key Sample')
257          .onClick(() => {
258            // 从PersistenceV2中删除后,prop将不会再与key为Sample的值关联
259            PersistenceV2.remove(Sample);
260          })
261
262        Button('Page1 save the key Sample')
263          .onClick(() => {
264            // 如果处于connect状态,持久化key为Sample的键值对
265            PersistenceV2.save(Sample);
266          })
267
268        Text(`Page1 add 1 to prop.p1: ${this.prop.f.p1}`)
269          .fontSize(30)
270          .onClick(() => {
271            this.prop.f.p1++;
272          })
273
274        Text(`Page1 add 1 to prop.p2: ${this.prop.f.p2}`)
275          .fontSize(30)
276          .onClick(() => {
277            // 页面不刷新,但是p2的值改变了
278            this.prop.f.p2++;
279          })
280
281        // 获取当前PersistenceV2里面的所有key
282        Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`)
283          .fontSize(30)
284      }
285      }
286  }
287}
288```
289
290页面2
291```ts
292// Page2.ets
293import { PersistenceV2 } from '@kit.ArkUI';
294import { Sample } from '../Sample';
295
296@Builder
297export function Page2Builder() {
298  Page2()
299}
300
301@ComponentV2
302struct Page2 {
303  // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
304  // 对于需要换connect对象的prop属性,需要加@Local修饰(不建议对属性换connect的对象)
305  @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!;
306  pathStack: NavPathStack = new NavPathStack();
307
308  build() {
309    NavDestination() {
310      Column() {
311        Button('Page2 connect the key Sample1')
312          .onClick(() => {
313            // 在PersistenceV2中创建一个key为Sample1的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
314            // 不建议对prop属性换connect的对象
315            this.prop = PersistenceV2.connect(Sample, 'Sample1', () => new Sample())!;
316          })
317
318        Text(`Page2 add 1 to prop.p1: ${this.prop.f.p1}`)
319          .fontSize(30)
320          .onClick(() => {
321            this.prop.f.p1++;
322          })
323
324        Text(`Page2 add 1 to prop.p2: ${this.prop.f.p2}`)
325          .fontSize(30)
326          .onClick(() => {
327            // 页面不刷新,但是p2的值改变了;只有重新初始化才会改变
328            this.prop.f.p2++;
329          })
330
331        // 获取当前PersistenceV2里面的所有key
332        Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`)
333          .fontSize(30)
334      }
335    }
336    .onReady((context: NavDestinationContext) => {
337      this.pathStack = context.pathStack;
338    })
339  }
340}
341```
342使用Navigation时,需要添加配置系统路由表文件src/main/resources/base/profile/route_map.json,并替换pageSourceFile为Page2页面的路径,并且在module.json5中添加:"routerMap": "$profile:route_map"。
343```json
344{
345  "routerMap": [
346    {
347      "name": "Page2",
348      "pageSourceFile": "src/main/ets/pages/Page2.ets",
349      "buildFunction": "Page2Builder",
350      "data": {
351        "description" : "PersistenceV2 example"
352      }
353    }
354  ]
355}
356```
357
358### 使用globalConnect存储数据
359
360```ts
361import { PersistenceV2, Type, ConnectOptions } from '@kit.ArkUI';
362import { contextConstant } from '@kit.AbilityKit';
363
364// 接受序列化失败的回调
365PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
366  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
367});
368
369@ObservedV2
370class SampleChild {
371  @Trace childId: number = 0;
372  groupId: number = 1;
373}
374
375@ObservedV2
376export class Sample {
377  // 对于复杂对象需要@Type修饰,确保序列化成功
378  @Type(SampleChild)
379  @Trace father: SampleChild = new SampleChild();
380}
381
382@Entry
383@ComponentV2
384struct Page1 {
385  @Local refresh: number = 0;
386  // key不传入尝试用为type的name作为key,加密参数不传入默认加密等级为EL2
387  @Local p: Sample = PersistenceV2.globalConnect({type: Sample, defaultCreator:() => new Sample()})!;
388
389  // 使用key:global1连接,传入加密等级为EL1
390  @Local p1: Sample = PersistenceV2.globalConnect({type: Sample, key:'global1', defaultCreator:() => new Sample(), areaMode: contextConstant.AreaMode.EL1})!;
391
392  // 使用key:global2连接,使用构造函数形式,加密参数不传入默认加密等级为EL2
393  options: ConnectOptions<Sample> = {type: Sample, key: 'global2', defaultCreator:() => new Sample()};
394  @Local p2: Sample = PersistenceV2.globalConnect(this.options)!;
395
396  // 使用key:global3连接,直接写加密数值,范围只能在0-4,否则运行会crash,例如加密设置为EL3
397  @Local p3: Sample = PersistenceV2.globalConnect({type: Sample, key:'global3', defaultCreator:() => new Sample(), areaMode: 3})!;
398
399  build() {
400    Column() {
401      /**************************** 显示数据 **************************/
402      // 被@Trace修饰的数据可以自动持久化进磁盘
403      Text('Key Sample: ' + this.p.father.childId.toString())
404        .onClick(()=> {
405          this.p.father.childId += 1;
406        })
407        .fontSize(25)
408        .fontColor(Color.Red)
409      Text('Key global1: ' + this.p1.father.childId.toString())
410        .onClick(()=> {
411          this.p1.father.childId += 1;
412        })
413        .fontSize(25)
414        .fontColor(Color.Red)
415      Text('Key global2: ' + this.p2.father.childId.toString())
416        .onClick(()=> {
417          this.p2.father.childId += 1;
418        })
419        .fontSize(25)
420        .fontColor(Color.Red)
421      Text('Key global3: ' + this.p3.father.childId.toString())
422        .onClick(()=> {
423          this.p3.father.childId += 1;
424        })
425        .fontSize(25)
426        .fontColor(Color.Red)
427      /**************************** keys接口 **************************/
428      // keys 本身不会刷新,需要借助状态变量刷新
429      Text('Persist keys: ' + PersistenceV2.keys().toString() + ' refresh: ' + this.refresh)
430        .onClick(() => {
431          this.refresh += 1;
432        })
433        .fontSize(25)
434
435      /**************************** remove接口 **************************/
436      Text('Remove key Sample: ' + 'refresh: ' + this.refresh)
437        .onClick(() => {
438          // 删除这个key,会导致和p失去联系,之后p无法存储,即使reconnect
439          PersistenceV2.remove(Sample);
440          this.refresh += 1;
441        })
442        .fontSize(25)
443      Text('Remove key global1: ' + 'refresh: ' + this.refresh)
444        .onClick(() => {
445          // 删除这个key,会导致和p失去联系,之后p无法存储,即使reconnect
446          PersistenceV2.remove('global1');
447          this.refresh += 1;
448        })
449        .fontSize(25)
450      Text('Remove key global2: ' + 'refresh: ' + this.refresh)
451        .onClick(() => {
452          // 删除这个key,会导致和p失去联系,之后p无法存储,即使reconnect
453          PersistenceV2.remove('global2');
454          this.refresh += 1;
455        })
456        .fontSize(25)
457      Text('Remove key global3: ' + 'refresh: ' + this.refresh)
458        .onClick(() => {
459          // 删除这个key,会导致和p失去联系,之后p无法存储,即使reconnect
460          PersistenceV2.remove('global3');
461          this.refresh += 1;
462        })
463        .fontSize(25)
464      /**************************** reConnect **************************/
465      // 重新连接也无法和之前的状态变量建立联系,因此无法保存数据
466      Text('ReConnect key global2: ' + 'refresh: ' + this.refresh)
467        .onClick(() => {
468          // 删除这个key,会导致和p失去联系,之后p无法存储,即使reconnect
469          PersistenceV2.globalConnect(this.options);
470          this.refresh += 1;
471        })
472        .fontSize(25)
473
474      /**************************** save接口 **************************/
475      Text('not save key Sample: ' + this.p.father.groupId.toString() + ' refresh: ' + this.refresh)
476        .onClick(() => {
477          // 未被@Trace保存的对象无法自动存储
478          this.p.father.groupId += 1;
479          this.refresh += 1;
480        })
481        .fontSize(25)
482      Text('save key Sample: ' + this.p.father.groupId.toString() + ' refresh: ' + this.refresh)
483        .onClick(() => {
484          // 未被@Trace保存的对象无法自动存储,需要调用key存储
485          this.p.father.groupId += 1;
486          PersistenceV2.save(Sample);
487          this.refresh += 1;
488        })
489        .fontSize(25)
490    }
491    .width('100%')
492  }
493}
494```
495
496### 在不同的module中使用connect和globalConnect
497
498**connect的存储路径需要注意以下两点:**
499
5001、connect使用module级别的存储路径,以最先启动的module的路径作为存储路径,从内存回写磁盘时会回写到第一个连接该module的路径。应用如果之后先从另一个module启动,则会以新module的路径作为存储路径。
501
5022、当不同module使用相同的key时,哪个module先启动,数据就为哪个module中保存的键值对,回写到对应的module中。
503
504**globalConnect的存储路径需要注意:**
505
506globalConnect虽然是应用级别的路径,但是可以设置不同的加密分区,不同加密分区即代表不同的存储路径。connect不支持设置加密分区,但是module自身切换加密级别时,module存储路径也会切换成对应加密分区路径。
507
508示例代码如下:开发者需要在项目基础上,新建一个module,并按照示例代码跳转到新module中。
509
510```ts
511// 模块1
512import { PersistenceV2, Type } from '@kit.ArkUI';
513import { contextConstant, common, Want } from '@kit.AbilityKit';
514
515// 接受序列化失败的回调
516PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
517  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
518});
519
520@ObservedV2
521class SampleChild {
522  @Trace childId: number = 0;
523  groupId: number = 1;
524}
525
526@ObservedV2
527export class Sample {
528  // 对于复杂对象需要@Type修饰,确保序列化成功
529  @Type(SampleChild)
530  @Trace father: SampleChild = new SampleChild();
531}
532
533@Entry
534@ComponentV2
535struct Page1 {
536  @Local refresh: number = 0;
537  // 使用key:global1连接,传入加密等级为EL1
538  @Local p1: Sample = PersistenceV2.globalConnect({type: Sample, key:'globalConnect1', defaultCreator:() => new Sample()})!;
539
540  // 使用key:global2连接,使用构造函数形式,加密参数不传入默认加密等级为EL2
541  @Local p2: Sample = PersistenceV2.connect(Sample, 'connect2', () => new Sample())!;
542
543  private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
544
545  build() {
546    Column() {
547      /**************************** 显示数据 **************************/
548      Text('Key globalConnect1: ' + this.p1.father.childId.toString())
549        .onClick(()=> {
550          this.p1.father.childId += 1;
551        })
552        .fontSize(25)
553        .fontColor(Color.Red)
554      Text('Key connect2: ' + this.p2.father.childId.toString())
555        .onClick(()=> {
556          this.p2.father.childId += 1;
557        })
558        .fontSize(25)
559        .fontColor(Color.Red)
560
561      /**************************** 跳转 **************************/
562      Button('跳转newModule').onClick(() => { // 不同module之间使用,建议使用globalConnect
563        let want: Want = {
564          deviceId: '', // deviceId为空代表本设备
565          bundleName: 'com.example.myPersistenceV2', // 在app.json5中查看
566          moduleName: 'newModule', // 在需要跳转的moudle的module.json5中查看,非必选参数
567          abilityName: 'NewModuleAbility',  // 跳转启动的abiltity,在跳转模块对应的ability.ets文件中查看
568          uri:'src/main/ets/pages/Index'
569        }
570        // context为调用方UIAbility的UIAbilityContext
571        this.context.startAbility(want).then(() => {
572          console.info('start ability success');
573        }).catch((err: Error) => {
574          console.error(`start ability failed. code is ${err.name}, message is ${err.message}`);
575        })
576      })
577    }
578    .width('100%')
579    .borderWidth(3)
580    .borderColor(Color.Blue)
581    .margin({top: 5, bottom: 5})
582  }
583}
584```
585
586```ts
587// 模块2
588import { PersistenceV2, Type } from '@kit.ArkUI';
589import { contextConstant } from '@kit.AbilityKit';
590
591// 接受序列化失败的回调
592PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
593  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
594});
595
596@ObservedV2
597class SampleChild {
598  @Trace childId: number = 0;
599  groupId: number = 1;
600}
601
602@ObservedV2
603export class Sample {
604  // 对于复杂对象需要@Type修饰,确保序列化成功
605  @Type(SampleChild)
606  @Trace father: SampleChild = new SampleChild();
607}
608
609@Entry
610@ComponentV2
611struct Page1 {
612  @Local a: number = 0;
613  // 使用key:global1连接,传入加密等级为EL1
614  @Local p1: Sample = PersistenceV2.globalConnect({type: Sample, key:'globalConnect1', defaultCreator:() => new Sample()})!;
615
616  // 使用key:global2连接,使用构造函数形式,加密参数不传入默认加密等级为EL2
617  @Local p2: Sample = PersistenceV2.connect(Sample, 'connect2', () => new Sample())!;
618
619  build() {
620    Column() {
621      /**************************** 显示数据 **************************/
622      Text('Key globalConnect1: ' + this.p1.father.childId.toString())
623        .onClick(()=> {
624          this.p1.father.childId += 1;
625        })
626        .fontSize(25)
627        .fontColor(Color.Red)
628      Text('Key connect2: ' + this.p2.father.childId.toString())
629        .onClick(()=> {
630          this.p2.father.childId += 1;
631        })
632        .fontSize(25)
633        .fontColor(Color.Red)
634    }
635    .width('100%')
636  }
637}
638```
639
640当开发者对newModule使用不同启动方式会有以下现象:
641
642*   开发者直接启动newModule,分别修改globalConnect1和connect2绑定的变量,例如将childId都改成5。
643* 应用退出并清空后台,启动模块entry,通过跳转按键启动newModule,会发现globalConnect1值为5,而connect2值为1未修改。
644* globalConnect为应用级别存储,对于一个key,整个应用在对应加密分区只有一份存储路径;connect为module级别的存储路径,会因为module的启动方式不同而在各自的加密分区对应不同的存储路径。
645
646## 使用建议
647
648建议开发者使用新接口globalConnect创建和获取数据。globalConnect的存储规格和内存规格一致,对于应用只有一份,并且支持设置加密级别,不需要去切换ability的加密才能设置数据的加密级别。当然如果开发者应用不涉及多模块,保持使用connect也不会有影响。
649
650### connect向globalConnect迁移实现
651
652```ts
653// 使用connect存储数据
654import { PersistenceV2, Type } from '@kit.ArkUI';
655
656// 接受序列化失败的回调
657PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
658  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
659});
660
661@ObservedV2
662class SampleChild {
663  @Trace childId: number = 0;
664  groupId: number = 1;
665}
666
667@ObservedV2
668export class Sample {
669  // 对于复杂对象需要@Type修饰,确保序列化成功
670  @Type(SampleChild)
671  @Trace father: SampleChild = new SampleChild();
672}
673
674@Entry
675@ComponentV2
676struct Page1 {
677  @Local refresh: number = 0;
678  // 使用key:connect2存储
679  @Local p: Sample = PersistenceV2.connect(Sample, 'connect2', () => new Sample())!;
680
681  build() {
682    Column({space: 5}) {
683      /**************************** 显示数据 **************************/
684      Text('Key connect2: ' + this.p.father.childId.toString())
685        .onClick(() => {
686          this.p.father.childId += 1;
687        })
688        .fontSize(25)
689        .fontColor(Color.Red)
690
691      /**************************** save接口 **************************/
692      // 非状态变量需要借助状态变量refresh才能刷新
693      Text('save key Sample: ' + this.p.father.groupId.toString() + ' refresh:' + this.refresh)
694        .onClick(() => {
695          // 未被@Trace保存的对象无法自动存储,需要调用key存储
696          this.p.father.groupId += 1;
697          PersistenceV2.save('connect2');
698          this.refresh += 1
699        })
700        .fontSize(25)
701    }
702    .width('100%')
703  }
704}
705```
706
707```ts
708// 迁移到globalConnect
709import { PersistenceV2, Type } from '@kit.ArkUI';
710
711// 接受序列化失败的回调
712PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
713  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
714});
715
716@ObservedV2
717class SampleChild {
718  @Trace childId: number = 0;
719  groupId: number = 1;
720}
721
722@ObservedV2
723export class Sample {
724  // 对于复杂对象需要@Type修饰,确保序列化成功
725  @Type(SampleChild)
726  @Trace father: SampleChild = new SampleChild();
727}
728
729// 用于判断是否完成数据迁移的辅助数据
730@ObservedV2
731class StorageState {
732  @Trace isCompleteMoving: boolean = false;
733}
734
735function move() {
736  let movingState = PersistenceV2.globalConnect({type: StorageState, defaultCreator: () => new StorageState()})!;
737  if (!movingState.isCompleteMoving) {
738    let p: Sample = PersistenceV2.connect(Sample, 'connect2', () => new Sample())!;
739    PersistenceV2.remove('connect2');
740    let p1 = PersistenceV2.globalConnect({type: Sample, key: 'connect2', defaultCreator: () => p})!;  // 使用默认构造函数也可以
741    // 赋值数据,@Trace修饰的会自动保存
742    p1.father = p.father;
743    // 将迁移标志设置为true
744    movingState.isCompleteMoving = true;
745  }
746}
747
748move();
749
750@Entry
751@ComponentV2
752struct Page1 {
753  @Local refresh: number = 0;
754  // 使用key:connect2存入数据
755  @Local p: Sample = PersistenceV2.globalConnect({type: Sample, key:'connect2', defaultCreator:() => new Sample()})!;
756
757  build() {
758    Column({space: 5}) {
759      /**************************** 显示数据 **************************/
760      Text('Key connect2: ' + this.p.father.childId.toString())
761        .onClick(() => {
762          this.p.father.childId += 1;
763        })
764        .fontSize(25)
765        .fontColor(Color.Red)
766
767      /**************************** save接口 **************************/
768      // 非状态变量需要借助状态变量refresh才能刷新
769      Text('save key connect2: ' + this.p.father.groupId.toString() + ' refresh:' + this.refresh)
770        .onClick(() => {
771          // 未被@Trace保存的对象无法自动存储,需要调用key存储
772          this.p.father.groupId += 1;
773          PersistenceV2.save('connect2');
774          this.refresh += 1
775        })
776        .fontSize(25)
777    }
778    .width('100%')
779  }
780}
781```
782
783connect向globalConnect迁移,需要将key绑定的value赋值给globalConnect进行存储,之后当自定义组件使用globalConnect连接时,globalConnect绑定的数据即为之前使用connect保存的数据,开发者可以自定义move函数,并将其放在合适位置迁移即可。