DEV Community

HarmonyOS
HarmonyOS

Posted on

What Can I Do If PersistentStorage Cannot Obtain Persistent Data?

Read the original article:What Can I Do If PersistentStorage Cannot Obtain Persistent Data?

What Can I Do If PersistentStorage Cannot Obtain Persistent Data?

Problem Description

When using PersistentStorage to operate on UI state variables, these variables are not correctly persisted. Each time the application is launched, the values are reinitialized. In the following example code, there are three UI state variables in the sample application, managed by LocalStorage, AppStorage, and PersistentStorage respectively. Among them, totalClickCount is managed by PersistentStorage and should be stored persistently. When the application is launched each time, the page should display the persisted data. However, in this example, the data cannot be retrieved properly and is always initialized to 0 upon each launch of the application.

Example code is as follows:

  • src/main/ets/const/StorageKeyConst.ets.
  export namespace StorageKeyConst {
    // Application-level UI state storage
    export const APP_PROP_KEY: string = 'APP_PROP';

    // Page-level UI state storage
    export const LOCAL_PROP_KEY: string = 'LOCAL_PROP';

    // Persistent UI state storage
    export const PERSIST_PROP_KEY: string = 'PERSIST_PROP';
  }
Enter fullscreen mode Exit fullscreen mode
  • src/main/ets/entryability/EntryAbility.ets.
   export default class EntryAbility extends UIAbility {
   onWindowStageCreate(windowStage: window.WindowStage): void {
      // Initialize application-level storage
      AppStorage.setOrCreate(StorageKeyConst.APP_PROP_KEY, 0);
      // Initialize application persistent storage
      PersistentStorage.persistProp(StorageKeyConst.PERSIST_PROP_KEY, 0);
      windowStage.loadContent('pages/Index', (err) => {
        if (err.code) {
          // Handling Exceptions
          return;
        }
      });
    }
  }
Enter fullscreen mode Exit fullscreen mode
  • src/main/ets/pages/Index.ets.
  import { StorageKeyConst } from '../const/StorageKeyConst';
  import { Router } from '@kit.ArkUI';

  let storage: LocalStorage = new LocalStorage();

  @Entry(storage)
  @Component
  struct Index {
    // Page No
    @State pageNo: string = '';
    // Page-level UI status storage (only saved on the current page)
    @LocalStorageLink(StorageKeyConst.LOCAL_PROP_KEY)
    pageClickCount: number = 0;
    // Application-level UI state storage (Saved in the app memory and released after the app exits)
    @StorageLink(StorageKeyConst.APP_PROP_KEY)
    startupClickCount: number = 0;
    // Persistent UI status storage (persistent storage, data retention after an app exits)
    @StorageLink(StorageKeyConst.PERSIST_PROP_KEY)
    totalClickCount: number = 0;
    router: Router = this.getUIContext().getRouter()

    aboutToAppear(): void {
      // Current Page No
      this.pageNo = this.router.getLength()
    }

    build() {
      RelativeContainer() {
        Column() {
          Text(`Current Page No:${this.pageNo}`)
            .margin(20)
          Text(`Number of clicks on the current page:${this.pageClickCount}`)
            .margin(10)
          Text(`Number of clicks in this startup:${this.startupClickCount}`)
            .margin(10)
          Text(`Total Hits:${this.totalClickCount}`)
            .margin(10)
          Button('Click!')
            .margin(10)
            .onClick(() => {
              // Click Count
              this.pageClickCount++
              this.startupClickCount++
              this.totalClickCount++
            })
          Button('New Page')
            .margin(10)
            .onClick(() => {
              this.router.pushUrl({ url: 'pages/Index' })
            })
        }
        .width('100%')
        .height('100%')
        .justifyContent(FlexAlign.Center)
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

Background Knowledge

  • LocalStorage: LocalStorage is a page-level UI state storage. Parameters received through the @Entry decorator can share the same LocalStorage instance within a page. LocalStorage supports state sharing among multiple pages within a UIAbility instance.
  • AppStorage: AppStorage is a global UI state storage for an application. It is bound to the application's process and is created by the UI framework when the application starts, providing a central storage for the application's UI state properties.
  • PersistentStorage: PersistentStorage is an optional singleton object in an application. This object is used to persistently store selected AppStorage properties to ensure that these properties retain their values after the application is restarted, just as they were when the application was closed. PersistentStorage is associated with a UI instance. Persistence operations can only be performed after the UI instance has been successfully initialized (i.e., when the callback passed to loadContent is called). Since the code in question calls PersistentStorage before this point, it leads to a failure in persistence. Refer to PersistentStorage: Persistently Storing UI State - Limitations.

Solution

There are two scenarios here. For debugging on a real device, you only need to modify the onWindowStageCreate function. However, since some APIs are not supported in the emulator, initializing PersistentStorage in onWindowStageCreate will fail. Therefore, you need to move the initialization code to the home page (Index.ets).

1.For the real device debugging scenario, modify the onWindowStageCreate function in src/main/ets/entryability/EntryAbility.ets:

    export default class EntryAbility extends UIAbility {
    onWindowStageCreate(windowStage: window.WindowStage): void {
       // Initialize application-level storage
       AppStorage.setOrCreate(StorageKeyConst.APP_PROP_KEY, 0);
       // Initialize application persistent storage
       PersistentStorage.persistProp(StorageKeyConst.PERSIST_PROP_KEY, 0);
       windowStage.loadContent('pages/Index', (err) => {
         if (err.code) {
           // Handling Exceptions
           return;
         }
       });
     }
   }
Enter fullscreen mode Exit fullscreen mode

2.In the simulator debugging scenario, move the initialization function stored in the onWindowStageCreate function of src/main/ets/entryability/EntryAbility.ets to the beginning of the file src/main/ets/pages/Index.ets.

   import { StorageKeyConst } from '../const/StorageKeyConst';
   import { Router } from '@kit.ArkUI';

   let storage: LocalStorage = new LocalStorage();

   @Entry(storage)
   @Component
   struct Index {
     // Page No
     @State pageNo: string = '';
     // Page-level UI status storage (only saved on the current page)
     @LocalStorageLink(StorageKeyConst.LOCAL_PROP_KEY)
     pageClickCount: number = 0;
     // Application-level UI state storage (Saved in the app memory and released after the app exits)
     @StorageLink(StorageKeyConst.APP_PROP_KEY)
     startupClickCount: number = 0;
     // Persistent UI status storage (persistent storage, data retention after an app exits)
     @StorageLink(StorageKeyConst.PERSIST_PROP_KEY)
     totalClickCount: number = 0;
     router: Router = this.getUIContext().getRouter()

     aboutToAppear(): void {
       // Current Page No
       this.pageNo = this.router.getLength()
     }

     build() {
       RelativeContainer() {
         Column() {
           Text(`Current Page No:${this.pageNo}`)
             .margin(20)
           Text(`Number of clicks on the current page:${this.pageClickCount}`)
             .margin(10)
           Text(`Number of clicks in this startup:${this.startupClickCount}`)
             .margin(10)
           Text(`Total Hits:${this.totalClickCount}`)
             .margin(10)
           Button('Click!')
             .margin(10)
             .onClick(() => {
               // Click Count
               this.pageClickCount++
               this.startupClickCount++
               this.totalClickCount++
             })
           Button('New Page')
             .margin(10)
             .onClick(() => {
               this.router.pushUrl({ url: 'pages/Index' })
             })
         }
         .width('100%')
         .height('100%')
         .justifyContent(FlexAlign.Center)
       }
     }
   }
Enter fullscreen mode Exit fullscreen mode

Verification Result

img
This example supports API Version 20 Release and later versions.
This example supports HarmonyOS 6.0.0 Release SDK and later versions.
This example needs to be compiled and run using DevEco Studio 6.0.0 Release or later.

Written by Emrecan Karakas

Top comments (0)