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.Set、collections.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函数,并将其放在合适位置迁移即可。