HarmonyOS 鸿蒙Next PersistentStorage数据持久化失败的原因之以及解决方法

发布于 1周前 作者 yibo5220 最后一次编辑是 5天前 来自 鸿蒙OS

1 背景及问题描述 最近在工作之余探索使用ArkTS进行纯血鸿蒙应用的开发,并计划完成一款纯血鸿蒙应用的上架。在开发过程中,在使用PersistentStorage实现登录状态、token的本地持久化过程中,遇到了持久化失败的问题,通过开发者联盟论坛,发现遇到该问题的有很多,很多没有明确的解决方法,参考官方文档与逐步摸索,解决了本次开发过程中持久化失败的问题。

1.1 问题描述 在登录页面完成登录后,应用程序进入登录状态,各个功能使用正常,但当应用后台被杀后,再次打开,仍为未登录状态,状态及token持久化失败。

1.2 开发环境 DevEco Studio版本:5.0.3.910

SDK版本:HarmonyOS 5.0.0 Release SDK,基于OpenHarmony SDK Ohos_sdk_public 5.0.0.71 (API Version 12 Release)

2 解决方法 2.1 持久化失败原因 经过反复阅读官方文档与论坛,定位原因是对PersistentStorage数据持久化的不理解,导致接口调用位置错误或接口使用错误,导致PersistentStorage的持久化运行错,从而导致数据持久化失败。

2.2 解决过程描述 在开始之前,首先附上PersistentStorage的官方文档,方便大家阅读,同时,后面的很多图片将会从该文档中进行引用PersistentStorage官方文档

2.2.1 PersistentStorage与AppStorage执行顺序错误 下图是PersistProp初始化流程,通过该流程可以判断,PersistentStorage的执行与AppStorage(应用级状态存储)是有先后执行顺序的,必须PersistentStorage执行结束后,才可以运行AppStorage,当执行顺序不正确时,AppStorage存储的值会将应用上次退出时持久化的值进行覆盖,从而导致每次打开应用,持久化内容都是失败的。

cke_3696.png

PersistentStorage与AppStorage的执行顺序错误,是本次开发过程中数据持久化的一个原因之一。

2.2.2 PersistentStorage的运行时机错误 PersistentStorage运行的时机也是至关重要的,这是本次开发持久化失败的第二个原因。

根据官方文档,PersistentStorage和UI实例相关联,持久化操作需要在UI实例初始化成功后(即loadContent传入的回调被调用时)才可以被调用,早于该时机调用会导致持久化失败。在本开发中,第一次将PersistentStorage放到了onCreate中,即应用刚创建时便执行持久化,导致了持久化的失败。

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
    PersistentStorage.persistProp("isLogin",false)
    PersistentStorage.persistProp("userId","")
    PersistentStorage.persistProp("username","")
    PersistentStorage.persistProp("token","")
    PersistentStorage.persistProp("nickname","")
  }

在第二次尝试时,将持久化放到了onDestroy中,及当应用销毁时,再执行,想法可以,但是当应用再次启动时,将会首先调用AppStorage,从而导致相关的数据被覆盖,从而导致持久化失败。

  onDestroy(): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
    PersistentStorage.persistProp("isLogin",false)
    PersistentStorage.persistProp("userId","")
    PersistentStorage.persistProp("username","")
    PersistentStorage.persistProp("token","")
    PersistentStorage.persistProp("nickname","")
  }

2.3 解决后使用方法

onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      PersistentStorage.persistProp("isLogin",false)
      PersistentStorage.persistProp("userId","")
      PersistentStorage.persistProp("username","")
      PersistentStorage.persistProp("token","")
      PersistentStorage.persistProp("nickname","")
}

代码中在使用如下方式进行状态更新(仅为示例)

//登录页面
@Entry
@Component
struct LoginPage{

  [@StorageLink](/user/StorageLink)("isLogin") isLogin:boolean=false;
  [@StorageLink](/user/StorageLink)("token") token:string|undefined='';
  [@StorageLink](/user/StorageLink)("userId") userId:string|undefined='';
  [@StorageLink](/user/StorageLink)("username") username:string|undefined='';
  [@StorageLink](/user/StorageLink)("nickname") nickname:string|undefined='';
    

   build() {
    Column(){

         Button("找回密码",{
        type:ButtonType.Capsule
      })
        .margin({top:10})
        .height(40)
        .width('100%')
        .backgroundColor("#E6A23C")
        .onClick(()=>{
            httpRequest("/users/loginByHuawei",http.RequestMethod.GET,httpCs,false).then((res:HttpResult)=>{
              console.log("请求内容为:"+res.getStatus())
              if (res.getStatus()===201) {
                // 未注册的处理内容
                this.userDomain.setUnionId(unionID)
                this.dialogController?.open()
                console.log("用户信息获取完成")
              }else if (res.getStatus()===200){
                this.isLogin=true;
                this.userId=res.getHs().get("userId");
                this.username=res.getHs().get("username");
                this.token=res.getHs().get("token");
                this.nickname=res.getHs().get("nickname");
                console.log("从appStrong中获取的username为:"+AppStorage.get("username"))
                console.log("从this中获取的username为:"+this.username)
                router.back()
              }
            })
            }
        })
    }
}

在非登录页面,使用如下方式实现页面状态的更新

  @StorageProp("nickname") nickname:string'';

之所以这样使用,因为 @StorageLink可以将状态更新,实现AppStorage中状态更新,而StorageProp仅是单项的,只可作用于本页面,AppStorage中的状态不会被改变。

3 总结 第一次接触鸿蒙开发,上述错误实属太低级。上述是开发过程中踩的一个大坑,是使用过程中常见的问题,但也会有其它问题导致持久化失败。当然,实现数据持久化,不光有这一种办法,这种办法或许是较为简单的一种,但灵活性可能较差。数据持久化也可使用ArkData(方舟数据管理),其提供了多种API,并实现了多种数据化持久化方式,相比来说更加灵活,但实现过程可能较为复杂。

鸿蒙开发纯小白,欢迎大家指正文字错误之处,也欢迎大家共同交流。

1 回复

作为IT专家,对于HarmonyOS 鸿蒙Next纯血开发中的PersistentStorage数据持久化失败问题,我有所了解。该问题的原因之一在于对PersistentStorage数据持久化的误解,导致接口调用位置错误或接口使用错误。

在鸿蒙Next开发中,PersistentStorage与AppStorage的执行顺序至关重要。PersistentStorage必须在AppStorage之前执行完毕,否则AppStorage存储的值会覆盖PersistentStorage持久化的值,导致每次打开应用时持久化内容失败。此外,PersistentStorage的调用时机也很关键,它必须与UI实例相关联,持久化操作需要在UI实例初始化成功后(即loadContent传入的回调被调用时)才能进行,早于该时机调用会导致持久化失败。

解决方法是在正确的时机和位置调用PersistentStorage。例如,在页面的@Entry上方进行初始化,或者在onWindowStageCreate回调中明确UIContext后调用。同时,要确保PersistentStorage的调用顺序在AppStorage之前。

在实际开发中,还需要注意PersistentStorage的存储容量限制,避免存储过大的数据导致失败。如果仍遇到问题,可以检查官方文档和论坛,确认接口使用是否正确,或者尝试清理存储后重新尝试。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部