# 使用PersistentStorage持久化数据常见问题场景和注意事项

#### 问题现象

*   场景一：如何解决嵌套对象更新PersistentStorage持久化数据失效的问题？
    
    ```
    class Student {
      name: string;
      age: number;
    
    
      constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
      }
    }
    
    
    class Students {
      students: Student[] = [new Student('Tom', 16), new Student('Gina', 18)];
    }
    
    
    PersistentStorage.persistProp('studentArr', new Students());
    
    
    @Entry
    @Component
    struct SceneOne {
      @StorageLink('studentArr') studentArrStr: Students | undefined = undefined;
    
    
      build() {
        Column({ space: 8 }) {
          ForEach(this.studentArrStr?.students, (item: Student) => {
            Column() {
              Text(`Student Name: ${item.name}`)
                .width('100%');
              Text(`Student Age: ${item.age}`)
                .width('100%');
            }
            .borderRadius(12)
            .width('100%')
            .backgroundColor('#f1f3f5')
            .padding(16)
            .onClick(() => {
              this.studentArrStr!.students[0].age = 999;
              this.studentArrStr!.students[1].age = 1000;
            });
          }, (item: Student) => JSON.stringify(item));
        }
        .width('100%')
        .height('100%')
        .padding(12);
      }
    }
    ```
    
*   场景二：如何解决PersistentStorage无法获取持久化的数据的问题？
*   场景三：如何解决PersistentStorage持久化数据无法删除的问题？

#### 背景知识

*   AppStorage：AppStorage是应用全局的UI状态存储，是和应用的进程绑定的，由UI框架在应用程序启动时创建，为应用程序UI状态属性提供中央存储。
*   PersistentStorage：PersistentStorage是应用程序中的可选单例对象。此对象的作用是持久化存储选定的AppStorage属性，以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。其一般使用问题可查看官网链接：PersistentStorage概述、PersistentStorage限制条件。

#### 解决方案

| 场景 | 场景描述 | 解决方案 |
| --- | --- | --- |
| 场景一 | 如何解决嵌套对象更新PersistentStorage持久化数据失效的问题？ | AppStorage不具备深度观测能力的限制，导致嵌套对象持久化失效，只能监听第一层变化。 |
| 场景二 | 如何解决PersistentStorage无法获取持久化的数据的问题？ | 初始化在loadContent之前导致获取失败，详情参考官网行业常见问题：PersistentStorage无法获取持久化的数据如何解决。 |
| 场景三 | 如何解决PersistentStorage持久化数据无法删除的问题？ | 存在被注册了的监听，导致无法删除，详情参考官网行业常见问题：PersistentStorage持久化数据无法删除。 |

*   场景一：如何解决嵌套对象更新PersistentStorage持久化数据失效的问题？
    
    在PersistentStorage持久化数据时一般是依赖AppStorage中的相关API进行持久化更新，例如上述问题代码中，通过@StorageLink获取的PersistentStorage持久化数据，当UI内修改该数据时，同步修改回PersistentStorage进行本地持久化。所以此时受AppStorage不具备深度观测能力的限制，直接修改嵌套的深层属性，不会更新回PersistentStorage进行本地持久化。修改后完整示例代码如下：
    
    ```
    class Student {
      name: string;
      age: number;
    
    
      constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
      }
    }
    
    
    class Students {
      students: Student[] = [new Student('Tom', 16), new Student('Gina', 18)];
    }
    
    
    PersistentStorage.persistProp('studentArr', new Students());
    
    
    @Entry
    @Component
    struct SceneOne {
      @StorageLink('studentArr') studentArrStr: Students | undefined = undefined;
    
    
      build() {
        Column({ space: 8 }) {
          ForEach(this.studentArrStr?.students, (item: Student) => {
            Column() {
              Text(`Student Name: ${item.name}`)
                .width('100%');
              Text(`Student Age: ${item.age}`)
                .width('100%');
            }
            .borderRadius(12)
            .width('100%')
            .backgroundColor('#f1f3f5')
            .padding(16)
            .onClick(() => {
              this.studentArrStr!.students = [new Student('Tom', 999), new Student('Gina', 1000)];
            });
          }, (item: Student) => JSON.stringify(item));
        }
        .width('100%')
        .height('100%')
        .padding(12);
      }
    }
    ```
    
    场景一实现效果如下：
    
*   场景二：如何解决PersistentStorage无法获取持久化的数据的问题？
    
    PersistentStorage和UI实例相关联，持久化操作需要在UI实例初始化成功后（即loadContent传入的回调被调用时）才可以被调用，由于问题代码中PersistentStorage早于该时机调用，所以会导致持久化失败。详情参考官网行业常见问题：PersistentStorage无法获取持久化的数据如何解决。
    
*   场景三：如何解决PersistentStorage持久化数据无法删除的问题？
    
    当有页面使用@StorageLink的装饰器注册了监听时，可能存在无法删除的情况，详情参考官网行业常见问题：PersistentStorage持久化数据无法删除。
    

#### 常见FAQ

Q：如果PersistentStorage中存在大量数据，清空操作是否会导致性能问题？如何优化？

A：PersistentStorage主要用于持久化存储UI状态和其他少量数据。根据文档，PersistentStorage的写入操作是同步的，并且最好用于存储小于2KB的数据。这是因为大量的数据持久化会影响UI渲染性能，特别是在UI线程中执行时。因此执行清空操作也会导致性能问题。优化方式参考如下：

1.  避免存储大量数据：尽可能减少PersistentStorage中的数据量，只存储必要的小量数据。
2.  使用数据库API：对于需要存储的大量数据，考虑使用数据库API而不是PersistentStorage。数据库可以在不影响应用性能的情况下更好地管理和存储大量数据。
3.  异步处理：尽管PersistentStorage的写入操作是同步的，但对于清空操作或其他批量操作，可以考虑在后台线程中进行，以避免影响主UI线程。

Q：使用了一个API来批量清空PersistentStorage中的数据，但返回了“UnsupportedOperationException”。这通常意味着什么？应该如何确保操作的支持？

A：“UnsupportedOperationException”通常在尝试调用一个不被当前环境或实现所支持的操作时抛出。这意味着使用的API可能不支持在当前的设备或系统环境中执行批量清空PersistentStorage的操作。确保操作的支持：

1.  检查API文档：确认使用的API是否明确支持批量清空操作，以及是否有关于系统环境的特定要求或限制。
2.  验证环境兼容性：确保设备或系统环境满足API运行的硬件和软件要求。

Q：如果PersistentStorage中的数据与其他组件或服务共享，清空操作前需要做哪些准备工作？

A：如果PersistentStorage中的数据与其他组件或服务共享，清空操作前建议备份数据。

Q：执行批量清空PersistentStorage数据时遇到了“NullPointerException”。应该如何排查和解决这个问题？

A：出现“NullPointerException”现象时，可参考以下原因排查：

1.  对象未初始化：在调用API之前，相关对象或组件没有正确初始化。
2.  传递的参数为空：向API传递的参数可能是空的，特别是在使用类实例或集合作为参数时。

解决方案参考如下：

1.  确保对象初始化：在调用API之前，确认所有对象都已正确创建并初始化。如果有必要，可以在调用API之前添加日志打印，以检查对象的状态。
2.  处理参数为空的情况：在传递参数给API之前，添加检查机制以确保参数不为空。如果参数来自用户输入或其他外部来源，可能需要更严格的输入验证。

Q：在使用PersistentStorage进行持久化存储UI状态时，有什么注意事项？

A：持久化数据是一个相对缓慢的操作，应用程序应避免以下情况：持久化大型数据集以及持久化经常变化的变量。

Q：如何监控PersistentStorage的使用情况，及时发现并处理异常访问？

A：可以通过以下方式监控PersistentStorage的使用情况：

1.  日志监控：使用日志记录功能，记录所有对PersistentStorage的读写操作。这样，你可以在日志中追踪异常访问的具体时间和地点。配置日志级别，使其记录详细的信息，包括操作类型、操作时间、操作对象等。
2.  自定义监听器：实现自定义监听器，监听PersistentStorage的读写事件。通过监听器，可以在操作发生时得到实时通知，并进行相应的处理。
3.  性能监控工具：使用性能监控工具，监控应用的性能，包括内存使用、CPU使用、网络流量等。通过性能监控工具，即使没有明显的异常访问，也能通过性能异常发现潜在问题。
4.  安全策略：设置安全策略，限制对PersistentStorage的访问。例如，可以设置访问权限，确保只有授权的应用和用户才能访问PersistentStorage。使用加密技术，对存储在PersistentStorage中的敏感数据进行加密，防止数据泄露。
5.  定期审查和备份：定期审查PersistentStorage中的数据，确保数据的准确性和完整性。定期备份PersistentStorage中的重要数据，防止数据丢失。