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的官方文档,方便大家阅读,同时,后面的很多图片将会从该文档中进行引用文档中心icon-default.png?t=O83Ahttps://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-persiststorage-V5

2.2.1  PersistentStorage与AppStorage执行顺序错误

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

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("isLogin") isLogin:boolean=false;
  @StorageLink("token") token:string|undefined='';
  @StorageLink("userId") userId:string|undefined='';
  @StorageLink("username") username:string|undefined='';
  @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,并实现了多种数据化持久化方式,相比来说更加灵活,但实现过程可能较为复杂。

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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐