1# PersistenceV2: Persisting Application State 2 3To enhance the state management framework's capability of persistently storing UIs, you can use **PersistenceV2** to persist data. 4 5**PersistenceV2** is an optional singleton object within an application. Its purpose is to persist UI-related data so that their values are the same upon application re-start as they were when the application was closed. 6 7**PersistenceV2** provides the state variable persistence capability. You can bind the same key through **connect** or **globalConnect** to implement the persistence capability during state variable change and application cold start. 8 9Before reading this topic, you are advised to read [\@ComponentV2](./arkts-new-componentV2.md), [\@ObservedV2 and \@Trace](./arkts-new-observedV2-and-trace.md), and API reference of [PersistentV2](../../reference/apis-arkui/js-apis-StateManagement.md#persistencev2). 10 11>**NOTE** 12> 13>**PersistenceV2** is supported since API version 12. 14> 15>**globalConnect** is supported since API version 18. The behavior of **globalConnect** is the same as that of **connect**. The only difference is that the underlying storage path of **connect** is a module-level path, while that of **globalConnect** is an application-level path. For details, see the section [Using connect and globalConnect in Different Modules](#using-connect-and-globalconnect-in-different-modules). 16 17 18## Overview 19 20**PersistenceV2** is a singleton to be created when the application UI is started. Its purpose is to provide central storage for application UI state attributes. Each attribute is accessed using a unique key, which is a string. Unlike **AppStorageV2**, **PersistenceV2** also persistently stores the latest data on device disks. In this way, the selected result can still be saved even when the application is closed. 21 22For a [\@ObservedV2](./arkts-new-observedV2-and-trace.md) object associated with **PersistenceV2**, the change of the [\@Trace](./arkts-new-observedV2-and-trace.md) attribute of the object triggers automatic persistence of the entire associated object. If necessary, you can call **PersistenceV2** APIs to manually perform persistence. 23 24**PersistenceV2** can synchronize application state attributes with UI components and can be accessed during implementation of application service logic as well. 25 26**PersistenceV2** supports state sharing among multiple UIAbility instances in the [main thread](../../application-models/thread-model-stage.md) of an application. 27 28## How to Use 29 30### connect: Creating or Obtaining Stored Data 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 | Description | 41| ------------ | ----------------------------------------------------- | 42| Parameter | **type**: specified type. If no **key** is specified, the name of the **type** is used as the **key**.<br> **keyOrDefaultCreator**: specified key or default constructor.<br> **defaultCreator**: default constructor. | 43| Return value | After creating or obtaining data, value is returned. Otherwise, **undefined** is returned.| 44 45>**NOTE** 46> 47>1. The second parameter is used when no **key** is specified or the second parameter is invalid, and the third parameter is used in all other cases. 48> 49>2. If the data has been stored in **PersistenceV2**, you can obtain the stored data without using the default constructor. Otherwise, you must specify the default constructor. If no constructor is specified, the application exception occurs. 50> 51>3. Ensure that the data types match the key. Connecting different types of data to the same key will result in an application exception. 52> 53>4. You are advised to use meaningful values for keys. The values can contain letters, digits, and underscores (_) and a maximum of 255 characters. Using invalid characters or null characters will result in undefined behavior. 54> 55>5. When matching the key with the [\@Observed](./arkts-observed-and-objectlink.md) object, specify the key or customize the **name** attribute. 56> 57>6. The data storage path is at the module level. That is, the data copy is stored in the persistent file of the corresponding module when the module calls the **connect** function. If multiple modules use the same key, the data of the module that uses the **connect** function first is used, and the data in **PersistenceV2** is also stored in the module that uses the **connect** function first. 58> 59>7. The storage path, determined when the first ability of the application is started, is the module to which the ability belongs. If an ability calls the **connect** function and can be started by different modules, the number of data copies is the same as the number of startup modes of the ability. 60 61### globalConnect: Creating or Obtaining Stored Data 62 63```ts 64// globalConnect API 65static globalConnect<T extends object>( 66 type: ConnectOptions<T> 67 ): T | undefined; 68``` 69 70```ts 71// ConnectOptions parameters 72class ConnectOptions<T extends object> { 73 type: TypeConstructorWithArgs<T>; // (Mandatory) Specified type. 74 key?: string; // (Optional) Input key. If no key is specified, the name of the type is used as the key. 75 defaultCreator?: StorageDefaultCreator<T> // Default constructor. You are advised to set this parameter. 76 areaMode?: contextConstant.AreaMode; // (Optional) Encryption parameter. 77} 78``` 79 80| globalConnect | Description | 81| ------------- | --------------------------------------------------------- | 82| Parameter | type: input parameter of **connect**. For details, see the description of **ConnectOptions**.| 83| Return value | After creating or obtaining data, value is returned. Otherwise, **undefined** is returned. | 84 85| ConnectOptions| Description | 86| :----------------: | :----------------------------------------------------------- | 87| type | **TypeConstructorWithArgs\<T\>**: (mandatory) specified type. | 88| key | Input key of the string type. If no value is passed in, the type name is used as the key. | 89| defaultCreator | **StorageDefaultCreator\<T\>**: default constructor. It is recommended that this parameter be passed in. If **globalConnect** is connected to the key for the first time, an error is reported if no parameter is passed in.| 90| areaMode | **contextConstant.AreaMode**: encryption level, ranging from EL1 to EL5 (corresponding to the value from 0 to 4). For details, see [Encryption Levels](../../application-models/application-context-stage.md#obtaining-and-modifying-encryption-levels). If no value is passed in, EL2 is used by default. Storage paths vary based on the encryption levels. If the input value of encryption level is not in the range of **0** to **4**, a crash occurs.| 91 92> **NOTE** 93> 94> 1. The second parameter is used when no **key** is specified or the second parameter is invalid, and the third parameter is used in all other cases. 95> 96> 2. If the data has been stored in **PersistenceV2**, you can obtain the stored data without using the default constructor. Otherwise, you must specify the default constructor. If no constructor is specified, the application exception occurs. 97> 98> 3. Ensure that the data types match the key. Matching different types of **globalConnect** data to the same key will result in an application exception. 99> 100> 4. You are advised to use meaningful values for keys. The values can contain letters, digits, and underscores (_) and a maximum of 255 characters. Using invalid characters or null characters will result in undefined behavior. 101> 102> 5. When matching the key with the [\@Observed](./arkts-observed-and-objectlink.md) object, specify the key or customize the **name** attribute. 103> 104> 6. Data is stored in an application-level path. Different modules use the same key and encryption level for **globalConnect**. Only one copy of data is stored. 105> 106> 7. **globalConnect** uses the same key but sets different encryption levels, in which the first-set encryption level is used. Data in **PersistenceV2** is also stored at the encryption level that uses the key first. 107> 108> 8. You are not advised to use **connect** and **globalConnect** together because the data copy paths are different. If they are used together, the keys must be different; otherwise, a crash occurs. 109> 110> 9. To make EL5 encryption level take effect, you need to configure the **ohos.permission.PROTECT_SCREEN_LOCK_DATA** field in the **module.json** file. For details, see [Declaring Permissions](../../security/AccessToken/declare-permissions.md). 111 112### remove: Deleting the Stored Data of a Specified Key 113 114```ts 115static remove<T>(keyOrType: string | TypeConstructorWithArgs<T>): void; 116``` 117 118| remove | Description | 119| ------------ | ----------------------------------------------------- | 120| Parameter | **keyOrType**: key to be deleted. If the key is of the **type**, the key to be deleted is the name of the **type**. | 121| Return value | None.| 122 123>**NOTE** 124> 125>If a key that does not exist in **PersistenceV2** is deleted, a warning is reported. 126 127### keys: Returning All Keys Stored in PersistenceV2 128 129```ts 130static keys(): Array<string>; 131``` 132 133| keys | Description | 134| ------------ | ----------------------------------------------------- | 135| Parameter | None. | 136| Return value | All keys in **PersistenceV2**.| 137 138> **NOTE** 139> 140> All keys in the module-level and application-level storage paths are returned. 141 142### save: Persisting Stored Data Manually 143 144```ts 145static save<T>(keyOrType: string | TypeConstructorWithArgs<T>): void; 146``` 147 148| save | Description | 149| ------------ | ----------------------------------------------------- | 150| Parameter | **keyOrType**: key that needs to be manually persist. If the key is of the **Type**, the key is the name of the **Type**. | 151| Return value | None.| 152 153>**NOTE** 154> 155>Changes to the non-[\@Trace](./arkts-new-observedV2-and-trace.md) data do not trigger **PersistenceV2**. If necessary, call this API to persist the data of the corresponding key. 156> 157>It is useless to manually persist the keys that are not in the **connect** state in the memory. 158 159 160### **notifyOnError**: Callback for Responding to a Serialization or Deserialization Failure 161 162```ts 163static notifyOnError(callback: PersistenceErrorCallback | undefined): void; 164``` 165 166| notifyOnError| Description | 167| ------------ | ----------------------------------------------------- | 168| Parameter | **callback**: When a serialization or deserialization fails, the callback is executed. Pass in **undefined** can cancel this callback.| 169| Return value | None.| 170 171>**NOTE** 172> 173>When data is stored to disks, the data needs to be serialized. If a key fails to be serialized, the error is unpredictable. As a result, this API can be called to capture exceptions. 174 175 176## Constraints 177 1781. This singleton must be used together with the UI thread only. Other threads, for example, @Sendable decorator is not supported. 179 1802. Types such as **collections.Set** and **collections.Map** are not supported. 181 1823. Non-buildin types, such as native PixelMap, NativePointer, and ArrayList types, are not supported. 183 1844. A single key supports a maximum of 8 KB data. If the data is too large, the persistence fails. 185 1865. The persistent data must be a class object. Containers, such as Array, Set, and Map, or objects of the built-in types, such as Date and Number, are not supported. 187 1886. Objects that used for loop reference are not supported. 189 1907. Automatic persistency is triggered only when [\@Trace](./arkts-new-observedV2-and-trace.md) data is changed. The change of state variables in V1, [\@Observed](./arkts-observed-and-objectlink.md) objects, and common data does not trigger persistency. 191 1928. Do not store a large amount of persistent data. Otherwise, frame freezing may occur. 193 1949. Do not use **connect** and **globalConnect** together. If you have to, their keys must be different; otherwise, a crash occurs. 195 196## Use Scenarios 197 198### Storing Data Between Two Pages 199 200Data page 201```ts 202// Sample.ets 203import { Type } from '@kit.ArkUI'; 204 205// Data center 206@ObservedV2 207class SampleChild { 208 @Trace p1: number = 0; 209 p2: number = 10; 210} 211 212@ObservedV2 213export class Sample { 214 // Complex objects need to be decorated by @Type to ensure successful serialization. 215 @Type(SampleChild) 216 @Trace f: SampleChild = new SampleChild(); 217} 218``` 219 220Page 1 221```ts 222// Page1.ets 223import { PersistenceV2 } from '@kit.ArkUI'; 224import { Sample } from '../Sample'; 225 226// Callback used to receive serialization failure. 227PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => { 228 console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`); 229}); 230 231@Entry 232@ComponentV2 233struct Page1 { 234 // Create a KV pair whose key is Sample in PersistenceV2 (if the key exists, the data in PersistenceV2 is returned) and associate it with prop. 235 // Add @Local to decorate the prop attribute that needs to change the connected object. (Changing the connected object is not recommended.) 236 @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!; 237 pageStack: NavPathStack = new NavPathStack(); 238 239 build() { 240 Navigation(this.pageStack) { 241 Column() { 242 Button('Go to page2') 243 .onClick(() => { 244 this.pageStack.pushPathByName('Page2', null); 245 }) 246 247 Button('Page1 connect the key Sample') 248 .onClick(() => { 249 // Create a KV pair whose key is Sample in PersistenceV2 (if the key exists, the data in PersistenceV2 is returned) and associate it with prop. 250 // Changing the connected object for the prop attribute is not recommended. 251 this.prop = PersistenceV2.connect(Sample, 'Sample', () => new Sample())!; 252 }) 253 254 Button('Page1 remove the key Sample') 255 .onClick(() => { 256 // After being deleted from PersistenceV2, prop will no longer be associated with the value whose key is Sample. 257 PersistenceV2.remove(Sample); 258 }) 259 260 Button('Page1 save the key Sample') 261 .onClick(() => { 262 // If the sample is in the connect state, persist the KV pair of the Sample. 263 PersistenceV2.save(Sample); 264 }) 265 266 Text(`Page1 add 1 to prop.p1: ${this.prop.f.p1}`) 267 .fontSize(30) 268 .onClick(() => { 269 this.prop.f.p1++; 270 }) 271 272 Text(`Page1 add 1 to prop.p2: ${this.prop.f.p2}`) 273 .fontSize(30) 274 .onClick(() => { 275 // The page is not re-rendered, but the value of p2 is changed. 276 this.prop.f.p2++; 277 }) 278 279 // Obtain all keys in the current PersistenceV2. 280 Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`) 281 .fontSize(30) 282 } 283 } 284 } 285} 286``` 287 288Page 2 289```ts 290// Page2.ets 291import { PersistenceV2 } from '@kit.ArkUI'; 292import { Sample } from '../Sample'; 293 294@Builder 295export function Page2Builder() { 296 Page2() 297} 298 299@ComponentV2 300struct Page2 { 301 // Create a KV pair whose key is Sample in PersistenceV2 (if the key exists, the data in PersistenceV2 is returned) and associate it with prop. 302 // Add @Local to decorate the prop attribute that needs to change the connected object. (Changing the connected object is not recommended.) 303 @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!; 304 pathStack: NavPathStack = new NavPathStack(); 305 306 build() { 307 NavDestination() { 308 Column() { 309 Button('Page2 connect the key Sample1') 310 .onClick(() => { 311 // Create a KV pair whose key is Sample1 in PersistenceV2 (if the key exists, the data in PersistenceV2 is returned) and associate it with prop. 312 // Changing the connected object for the prop attribute is not recommended. 313 this.prop = PersistenceV2.connect(Sample, 'Sample1', () => new Sample())!; 314 }) 315 316 Text(`Page2 add 1 to prop.p1: ${this.prop.f.p1}`) 317 .fontSize(30) 318 .onClick(() => { 319 this.prop.f.p1++; 320 }) 321 322 Text(`Page2 add 1 to prop.p2: ${this.prop.f.p2}`) 323 .fontSize(30) 324 .onClick(() => { 325 // The page is not re-rendered, but the value of p2 is changed, which is performed after re-initialization. 326 this.prop.f.p2++; 327 }) 328 329 // Obtain all keys in the current PersistenceV2. 330 Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`) 331 .fontSize(30) 332 } 333 } 334 .onReady((context: NavDestinationContext) => { 335 this.pathStack = context.pathStack; 336 }) 337 } 338} 339``` 340When using **Navigation**, you need to add the **route_map.json** file to the **src/main/resources/base/profile** directory, replace the value of **pageSourceFile** with the path of **Page2**, and add **"routerMap": "$profile: route_map"** to the **module.json5** file. 341```json 342{ 343 "routerMap": [ 344 { 345 "name": "Page2", 346 "pageSourceFile": "src/main/ets/pages/Page2.ets", 347 "buildFunction": "Page2Builder", 348 "data": { 349 "description" : "PersistenceV2 example" 350 } 351 } 352 ] 353} 354``` 355 356### Using globalConnect to Store Data 357 358```ts 359import { PersistenceV2, Type, ConnectOptions } from '@kit.ArkUI'; 360import { contextConstant } from '@kit.AbilityKit'; 361 362// Callback used to receive serialization failure. 363PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => { 364 console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`); 365}); 366 367@ObservedV2 368class SampleChild { 369 @Trace childId: number = 0; 370 groupId: number = 1; 371} 372 373@ObservedV2 374export class Sample { 375 // Complex objects need to be decorated by @Type to ensure successful serialization. 376 @Type(SampleChild) 377 @Trace father: SampleChild = new SampleChild(); 378} 379 380@Entry 381@ComponentV2 382struct Page1 { 383 @Local refresh: number = 0; 384 // Use the type name as the key if no key is passed in; use EL2 as the default encryption level if no encryption parameter is passed in. 385 @Local p: Sample = PersistenceV2.globalConnect({type: Sample, defaultCreator:() => new Sample()})!; 386 387 // Use key:global1 for connection and set the encryption level to EL1. 388 @Local p1: Sample = PersistenceV2.globalConnect({type: Sample, key:'global1', defaultCreator:() => new Sample(), areaMode: contextConstant.AreaMode.EL1})!; 389 390 // Use key:global2 for connection and use the constructor function. If no encryption parameter is passed in, EL2 is used by default. 391 options: ConnectOptions<Sample> = {type: Sample, key: 'global2', defaultCreator:() => new Sample()}; 392 @Local p2: Sample = PersistenceV2.globalConnect(this.options)!; 393 394 // Use key:global3 for connection and set the encryption parameter ranging from 0 to 4; otherwise, a crash occurs. In this case, EL3 is set. 395 @Local p3: Sample = PersistenceV2.globalConnect({type: Sample, key:'global3', defaultCreator:() => new Sample(), areaMode: 3})!; 396 397 build() { 398 Column() { 399 /**************************** Display data **************************/ 400 // Data decorated by @Trace can be automatically persisted to disks. 401 Text('Key Sample: ' + this.p.father.childId.toString()) 402 .onClick(()=> { 403 this.p.father.childId += 1; 404 }) 405 .fontSize(25) 406 .fontColor(Color.Red) 407 Text('Key global1: ' + this.p1.father.childId.toString()) 408 .onClick(()=> { 409 this.p1.father.childId += 1; 410 }) 411 .fontSize(25) 412 .fontColor(Color.Red) 413 Text('Key global2: ' + this.p2.father.childId.toString()) 414 .onClick(()=> { 415 this.p2.father.childId += 1; 416 }) 417 .fontSize(25) 418 .fontColor(Color.Red) 419 Text('Key global3: ' + this.p3.father.childId.toString()) 420 .onClick(()=> { 421 this.p3.father.childId += 1; 422 }) 423 .fontSize(25) 424 .fontColor(Color.Red) 425 /**************************** The keys API **************************/ 426 // keys() is not updated by itself. You need to use the state variable to update it. 427 Text('Persist keys: ' + PersistenceV2.keys().toString() + ' refresh: ' + this.refresh) 428 .onClick(() => { 429 this.refresh += 1; 430 }) 431 .fontSize(25) 432 433 /**************************** The remove API **************************/ 434 Text('Remove key Sample: ' + 'refresh: ' + this.refresh) 435 .onClick(() => { 436 // Removing this key will disconnect from PersistenceV2. After that, PersistenceV2 cannot store data even if it is reconnected. 437 PersistenceV2.remove(Sample); 438 this.refresh += 1; 439 }) 440 .fontSize(25) 441 Text('Remove key global1: ' + 'refresh: ' + this.refresh) 442 .onClick(() => { 443 // Removing this key will disconnect from PersistenceV2. After that, PersistenceV2 cannot store data even if it is reconnected. 444 PersistenceV2.remove('global1'); 445 this.refresh += 1; 446 }) 447 .fontSize(25) 448 Text('Remove key global2: ' + 'refresh: ' + this.refresh) 449 .onClick(() => { 450 // Removing this key will disconnect from PersistenceV2. After that, PersistenceV2 cannot store data even if it is reconnected. 451 PersistenceV2.remove('global2'); 452 this.refresh += 1; 453 }) 454 .fontSize(25) 455 Text('Remove key global3: ' + 'refresh: ' + this.refresh) 456 .onClick(() => { 457 // Removing this key will disconnect from PersistenceV2. After that, PersistenceV2 cannot store data even if it is reconnected. 458 PersistenceV2.remove('global3'); 459 this.refresh += 1; 460 }) 461 .fontSize(25) 462 /**************************** reConnect **************************/ 463 // Fail to connect to the previous state variable after reconnection. Therefore, data cannot be saved. 464 Text('ReConnect key global2: ' + 'refresh: ' + this.refresh) 465 .onClick(() => { 466 // Removing this key will disconnect from PersistenceV2. After that, PersistenceV2 cannot store data even if it is reconnected. 467 PersistenceV2.globalConnect(this.options); 468 this.refresh += 1; 469 }) 470 .fontSize(25) 471 472 /**************************** The save API **************************/ 473 Text('not save key Sample: ' + this.p.father.groupId.toString() + ' refresh: ' + this.refresh) 474 .onClick(() => { 475 // Objects that are not saved by @Trace cannot be automatically stored. 476 this.p.father.groupId += 1; 477 this.refresh += 1; 478 }) 479 .fontSize(25) 480 Text('save key Sample: ' + this.p.father.groupId.toString() + ' refresh: ' + this.refresh) 481 .onClick(() => { 482 // Objects that are not saved by @Trace cannot be automatically stored. You need to call the key for storage. 483 this.p.father.groupId += 1; 484 PersistenceV2.save(Sample); 485 this.refresh += 1; 486 }) 487 .fontSize(25) 488 } 489 .width('100%') 490 } 491} 492``` 493 494### Using connect and globalConnect in Different Modules 495 496**For the storage path of connect:** 497 4981. **connect** uses the first-started module path as the storage path and data is written back from the memory to this storage path in the disk. If the application is started from another module, the path of the new module is used as the storage path. 499 5002. When different modules use the same key, the key-value pair stored in the module that is started first is written back to the corresponding module. 501 502**For the storage path of GlobalConnect:** 503 504Although **globalConnect** is an application-level path, different encryption levels can be set, indicating different storage paths. **connect** does not support the setting of the encryption level. However, when the encryption level of the module is switched, the module storage path is also switched to the path of the corresponding encryption level. 505 506Create a module based on the project and redirect to the new module based on the sample code. The sample code is as follows: 507 508```ts 509// Module 1 510import { PersistenceV2, Type } from '@kit.ArkUI'; 511import { contextConstant, common, Want } from '@kit.AbilityKit'; 512 513// Callback used to receive serialization failure. 514PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => { 515 console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`); 516}); 517 518@ObservedV2 519class SampleChild { 520 @Trace childId: number = 0; 521 groupId: number = 1; 522} 523 524@ObservedV2 525export class Sample { 526 // Complex objects need to be decorated by @Type to ensure successful serialization. 527 @Type(SampleChild) 528 @Trace father: SampleChild = new SampleChild(); 529} 530 531@Entry 532@ComponentV2 533struct Page1 { 534 @Local refresh: number = 0; 535 // Use key:global1 for connection and set the encryption level to EL1. 536 @Local p1: Sample = PersistenceV2.globalConnect({type: Sample, key:'globalConnect1', defaultCreator:() => new Sample()})!; 537 538 // Use key:global2 for connection and use the constructor function. If no encryption parameter is passed in, EL2 is used by default. 539 @Local p2: Sample = PersistenceV2.connect(Sample, 'connect2', () => new Sample())!; 540 541 private context = getContext(this) as common.UIAbilityContext; 542 543 build() { 544 Column() { 545 /**************************** Display data **************************/ 546 Text('Key globalConnect1: ' + this.p1.father.childId.toString()) 547 .onClick(()=> { 548 this.p1.father.childId += 1; 549 }) 550 .fontSize(25) 551 .fontColor(Color.Red) 552 Text('Key connect2: ' + this.p2.father.childId.toString()) 553 .onClick(()=> { 554 this.p2.father.childId += 1; 555 }) 556 .fontSize(25) 557 .fontColor(Color.Red) 558 559 /**************************** Redirection **************************/ 560 Button('Redirect to newModule').onClick(() => { // Used between different modules. You are advised to use globalConnect. 561 let want: Want = { 562 deviceId: '', // If deviceId is empty, the device is the local device. 563 bundleName: 'com.example.myPersistenceV2', // Check it in app.json5. 564 moduleName: 'newModule', // Check this optional value in the module.json5 file of the module to be redirected to. 565 abilityName: 'NewModuleAbility', // Redirect to the ability to start. You can check the ability name in the ability.ets file of the target module. 566 uri:'src/main/ets/pages/Index' 567 } 568 // context is the UIAbilityContext of the initiator UIAbility. 569 this.context.startAbility(want).then(() => { 570 console.info('start ability success'); 571 }).catch((err: Error) => { 572 console.error(`start ability failed. code is ${err.name}, message is ${err.message}`); 573 }) 574 }) 575 } 576 .width('100%') 577 .borderWidth(3) 578 .borderColor(Color.Blue) 579 .margin({top: 5, bottom: 5}) 580 } 581} 582``` 583 584```ts 585// Module 2 586import { PersistenceV2, Type } from '@kit.ArkUI'; 587import { contextConstant } from '@kit.AbilityKit'; 588 589// Callback used to receive serialization failure. 590PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => { 591 console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`); 592}); 593 594@ObservedV2 595class SampleChild { 596 @Trace childId: number = 0; 597 groupId: number = 1; 598} 599 600@ObservedV2 601export class Sample { 602 // Complex objects need to be decorated by @Type to ensure successful serialization. 603 @Type(SampleChild) 604 @Trace father: SampleChild = new SampleChild(); 605} 606 607@Entry 608@ComponentV2 609struct Page1 { 610 @Local a: number = 0; 611 // Use key:global1 for connection and set the encryption level to EL1. 612 @Local p1: Sample = PersistenceV2.globalConnect({type: Sample, key:'globalConnect1', defaultCreator:() => new Sample()})!; 613 614 // Use key:global2 for connection and use the constructor function. If no encryption parameter is passed in, EL2 is used by default. 615 @Local p2: Sample = PersistenceV2.connect(Sample, 'connect2', () => new Sample())!; 616 617 build() { 618 Column() { 619 /**************************** Display data **************************/ 620 Text('Key globalConnect1: ' + this.p1.father.childId.toString()) 621 .onClick(()=> { 622 this.p1.father.childId += 1; 623 }) 624 .fontSize(25) 625 .fontColor(Color.Red) 626 Text('Key connect2: ' + this.p2.father.childId.toString()) 627 .onClick(()=> { 628 this.p2.father.childId += 1; 629 }) 630 .fontSize(25) 631 .fontColor(Color.Red) 632 } 633 .width('100%') 634 } 635} 636``` 637 638When you use different startup modes for newModule, the following symptoms may occur: 639 640* Start the **newModule** and change the variables bound to **globalConnect1** and **connect2**. For example, change the value of **childId** to **5**. 641* Exit the application, clear the background, start the module entry, and start **newModule** by pressing the redirection key. The value of **globalConnect1** is **5**, and the value of **connect2** remains **1**. 642* **globalConnect** is an application-level storage path. For a key, the entire application has only one storage path for the corresponding encryption level. **connect** is a module-level storage path. Each encryption level has a different storage path according to the startup mode of the module. 643 644## Suggestions 645 646You are advised to use the new API **globalConnect** to create and obtain data. The storage specifications and memory specifications of **globalConnect** are the same for an application, and the encryption level can be set without switching the encryption mode of ability. If your application does not involve multiple modules, using **connect** will not affect your application. 647 648### Migrating from connect to globalConnect 649 650```ts 651// Use connect to store data. 652import { PersistenceV2, Type } from '@kit.ArkUI'; 653 654// Callback used to receive serialization failure. 655PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => { 656 console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`); 657}); 658 659@ObservedV2 660class SampleChild { 661 @Trace childId: number = 0; 662 groupId: number = 1; 663} 664 665@ObservedV2 666export class Sample { 667 // Complex objects need to be decorated by @Type to ensure successful serialization. 668 @Type(SampleChild) 669 @Trace father: SampleChild = new SampleChild(); 670} 671 672@Entry 673@ComponentV2 674struct Page1 { 675 @Local refresh: number = 0; 676 // Use key:connect2 to store data. 677 @Local p: Sample = PersistenceV2.connect(Sample, 'connect2', () => new Sample())!; 678 679 build() { 680 Column({space: 5}) { 681 /**************************** Display data **************************/ 682 Text('Key connect2: ' + this.p.father.childId.toString()) 683 .onClick(() => { 684 this.p.father.childId += 1; 685 }) 686 .fontSize(25) 687 .fontColor(Color.Red) 688 689 /**************************** The save API **************************/ 690 // Non-state variables can be refreshed only by using the state variable refresh. 691 Text('save key Sample: ' + this.p.father.groupId.toString() + ' refresh:' + this.refresh) 692 .onClick(() => { 693 // Objects that are not saved by @Trace cannot be automatically stored. You need to call the key for storage. 694 this.p.father.groupId += 1; 695 PersistenceV2.save('connect2'); 696 this.refresh += 1 697 }) 698 .fontSize(25) 699 } 700 .width('100%') 701 } 702} 703``` 704 705```ts 706// Migrate to GlobalConnect. 707import { PersistenceV2, Type } from '@kit.ArkUI'; 708 709// Callback used to receive serialization failure. 710PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => { 711 console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`); 712}); 713 714@ObservedV2 715class SampleChild { 716 @Trace childId: number = 0; 717 groupId: number = 1; 718} 719 720@ObservedV2 721export class Sample { 722 // Complex objects need to be decorated by @Type to ensure successful serialization. 723 @Type(SampleChild) 724 @Trace father: SampleChild = new SampleChild(); 725} 726 727// Auxiliary data used to determine whether data migration is complete 728@ObservedV2 729class StorageState { 730 @Trace isCompleteMoving: boolean = false; 731} 732 733function move() { 734 let movingState = PersistenceV2.globalConnect({type: StorageState, defaultCreator: () => new StorageState()})!; 735 if (!movingState.isCompleteMoving) { 736 let p: Sample = PersistenceV2.connect(Sample, 'connect2', () => new Sample())!; 737 PersistenceV2.remove('connect2'); 738 let p1 = PersistenceV2.globalConnect({type: Sample, key: 'connect2', defaultCreator: () = > p})!; // You can use the default constructor. 739 // For assigned value decorated by @Trace, it is automatically saved. 740 p1.father = p.father; 741 // Set the migration flag to true. 742 movingState.isCompleteMoving = true; 743 } 744} 745 746move(); 747 748@Entry 749@ComponentV2 750struct Page1 { 751 @Local refresh: number = 0; 752 // Use key:connect2 to store data. 753 @Local p: Sample = PersistenceV2.globalConnect({type: Sample, key:'connect2', defaultCreator:() => new Sample()})!; 754 755 build() { 756 Column({space: 5}) { 757 /**************************** Display data **************************/ 758 Text('Key connect2: ' + this.p.father.childId.toString()) 759 .onClick(() => { 760 this.p.father.childId += 1; 761 }) 762 .fontSize(25) 763 .fontColor(Color.Red) 764 765 /**************************** The save API **************************/ 766 // Non-state variables can be refreshed only by using the state variable refresh. 767 Text('save key connect2: ' + this.p.father.groupId.toString() + ' refresh:' + this.refresh) 768 .onClick(() => { 769 // Objects that are not saved by @Trace cannot be automatically stored. You need to call the key for storage. 770 this.p.father.groupId += 1; 771 PersistenceV2.save('connect2'); 772 this.refresh += 1 773 }) 774 .fontSize(25) 775 } 776 .width('100%') 777 } 778} 779``` 780 781To migrate data from **connect** to **globalConnect**, you should assign the value bound to the key to **globalConnect** for storage. When the custom component uses **globalConnect** for connection, the data bound to **globalConnect** is the data saved using **connect**. You can customize the **move** function and move the data to a proper position. 782