目录

应用全局UI状态存储和持久化V2版本

AppStorageV2

connect

remove

keys

示例

使用限制

PersistenceV2

connect

remove

keys

save

notifyOnError

 示例

使用限制

@Type

 使用限制


应用全局UI状态存储和持久化V2版本

以下实例AppStorageV2、PersistenceV2和装饰器@Type都需要搭配@ComponentV2使用。

AppStorageV2

AppStorageV2是在应用UI启动时会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。AppStorageV2将在应用运行过程保留其数据。数据通过唯一的键字符串值访问。

AppStorageV2支持应用的主线程内多个UIAbility实例间的状态共享

connect

用于在AppStorageV2实例中创建键值对数据或者获取实例中key对应的值,返回创建或获取的值(类实例|undefinded)。返回的不为undefined情况下,该值改变,对应AppStorageV2存储中的值也会改变。

AppStorageV2.connect<T extends object>(type: TypeConstructorWithArgs<T>, keyOrDefaultCreator?: string | StorageDefaultCreator<T>, defaultCreator?: StorageDefaultCreator<T>): T | undefined;
参数名 类型 必填 说明
type TypeConstructorWithArgs<T> 指定的类型,若未指定key,则使用type的name作为key。
keyOrDefaultCreator string | StorageDefaultCreator<T> 指定的key,或者是获取默认值的构造器。
defaultCreator StorageDefaultCreator<T> 获取默认值的构造器。

有4种形式入参:

AppStorageV2.connect(Sample)

1个参数,类名。相当于AppStorageV2.connect(Sample,"Sample")。

AppStorageV2.connect(Sample,"Sample1")

2个参数,类名+字符串。表示获取AppStorageV2中"Sample1"对应的值,该值的类型为Sample,获取不到返回undefined。

AppStorageV2.connect(Sample,()=>new Sample())

2个参数,类名+默认值构造器。相当于AppStorageV2.connect(Sample,"Sample",()=>new Sample)

AppStorageV2.connect(Sample,"Sample2",()=>new Sample())

3个参数,类名+字符串+默认值构造器。先获取AppStorageV2中"Sample2"对应的值,如果获取不到,则通过调用默认值构造器,将结果存入AppStorageV2值中,key为"Sample2"。

注意:使用时,该方法可能返回undefined,故在给@Local等装饰器修饰的变量赋值时需要在最后加上感叹号!,用来避过undefined的类型检测,表示我确认不会返回undefined。

 

remove

用于删除AppStorageV2实例中指定的键值对数据,返回undefined。

AppStorageV2.remove<T>(keyOrType: string | TypeConstructorWithArgs<T>): void
参数名 类型 必填 说明
keyOrType string | TypeConstructorWithArgs<T> 需要删除的key;如果指定的是type类型,删除的key为type的name。

只有一种入参方式,可以传类名或者字符串。传类名表示直接删除类名对应字符串后的键值对。 

注意:删除AppStorageV2中不存在的key会有Warning提示The key to be deleted does not exist。

keys

AppStorageV2.keys(): Array<string>

返回AppStorageV2实例中的所有key(Array<string>)。

示例

Navigation组件跳转页面时,首页不会重复渲染,返回不会重复渲染,pushPathByName会重新渲染新页面,故下例中首页只会渲染一次,每次跳到page2页面都会渲染一个新的。

1.点击Page1 p1、Page2 p2,p1数字会变动,p2不会,由于@Trace只装饰了p1,p2的实际值增加了,但是由于UI没刷新(在第2步中可以看出来)。

2.点击跳转page2,p1、p2的数字都同步过去了,由于都是在AppStorageV2实例中获取的Sample键值对。

3.点击Page2中按钮,在AppStorageV2中创建一个新的键值对Sample1,当前页面p1、p2的值会还原。

4.点击返回,由于首页不会刷新,这时无变化。

5.点击all keys in AppStorage,触发该段文字ui刷新,发现多了一个Sample1的key。

6.点击remove key按钮AppStorageV2中的Sample键值对删除了。

7.点击跳转page2,发现应用直接报错了,这是由于page2页面重新渲染,其中下面代码AppStorageV2.connect(Sample)返回undefined,@Local不能初始化值传入undefined。

@Local prop: Sample = AppStorageV2.connect(Sample)!;

注意:需要在真机环境运行,在预览环境会报错。

Index.ets页面

// Index.ets
import { AppStorageV2 } from '@kit.ArkUI';

// 数据中心
@ObservedV2
export class Sample {
  @Trace p1: number = 0;
  p2: number = 10;
}

@Entry
@ComponentV2
struct Page1 {
  // 在AppStorageV2中创建一个key为Sample的键值对(如果存在,则返回AppStorageV2中的数据),并且和prop关联
  @Local prop: Sample = AppStorageV2.connect(Sample,'Sample',()=>new Sample())!;
  @Local prop1: String = "1"
  pageStack: NavPathStack = new NavPathStack();
  fontSize(){
    console.log("all keys in AppStorage:渲染了一次")
    return 30
  }
  build() {
    Navigation(this.pageStack) {
      Column() {
        Button('Go to page2')
          .onClick(() => {
            this.pageStack.pushPathByName('Page2', null);
          })
        Button('Page1 connect the key Sample')
          .onClick(() => {
            // 在AppStorageV2中创建一个key为Sample的键值对(如果存在,则返回AppStorageV2中的数据),并且和prop关联
            this.prop = AppStorageV2.connect(Sample,()=>new Sample())!;
          })
        Button('Page1 remove the key Sample')
          .onClick(() => {
            // 从AppStorageV2中删除后,prop将不会再与key为Sample的值关联
            AppStorageV2.remove(Sample);
          })
        Text(`Page1 p1: ${this.prop.p1}`)
          .fontSize(30)
          .onClick(() => {
            this.prop.p1++;
          })

        Text(`Page1 p2: ${this.prop.p2}`)
          .fontSize(30)
          .onClick(() => {
            // 页面不刷新,但是p2的值改变了
            this.prop.p2++;
          })

        // 获取当前AppStorageV2里面的所有key
        Text(`${this.prop1}all keys in AppStorage: ${AppStorageV2.keys()}`)
          .fontSize(this.fontSize())
          .onClick(() => {
            // 页面不刷新,但是p2的值改变了
            this.prop1 = this.prop1+'1'
          })
      }
    }
  }
}

Index2.ets页面

// Page2.ets
import { AppStorageV2 } from '@kit.ArkUI';
import { Sample } from './Index';

@Builder
export function Page2Builder() {
  Page2()
}

@ComponentV2
struct Page2 {
  // 在AppStorageV2中创建一个key为Sample的键值对(如果存在,则返回AppStorageV2中的数据),并且和prop关联
  @Local prop: Sample = AppStorageV2.connect(Sample)!;
  pathStack: NavPathStack = new NavPathStack();
  fontSize(){
    console.log("渲染了一次11")
    return 30
  }
  build() {
    NavDestination() {
      Column() {
        Button('Page2 connect the key Sample1')
          .onClick(() => {
            // 在AppStorageV2中创建一个key为Sample1的键值对(如果存在,则返回AppStorageV2中的数据),并且和prop关联
            this.prop = AppStorageV2.connect(Sample,"Sample1",()=>new Sample())!;
          })

        Text(`Page2 p1: ${this.prop.p1}`)
          .fontSize(30)
          .onClick(() => {
            this.prop.p1++;
          })

        Text(`Page2 p2: ${this.prop.p2}`)
          .fontSize(this.fontSize())
          .onClick(() => {
            // 页面不刷新,但是p2的值改变了;只有重新初始化才会改变
            this.prop.p2++;
          })

        // 获取当前AppStorageV2里面的所有key
        Text(`all keys in AppStorage: ${AppStorageV2.keys()}`)
          .fontSize(30)
      }
    }
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
    })
  }
}

使用Navigation组件时,需要添加配置系统路由表文件,先在module.json5中添加:"routerMap": "$profile:route_map",表示使用src/main/resources/base/profile目录下的route_map.json文件作为路由表。故在该目录下创建文件route_map.json。

{
  "routerMap": [
    {
      "name": "Page2",
      "pageSourceFile": "src/main/ets/pages/Index2.ets",
      "buildFunction": "Page2Builder",
      "data": {
        "description" : "AppStorageV2 example"
      }
    }
  ]
}

使用限制

1、需要配合UI使用(UI线程),不能在其他线程使用,如不支持@Sendable。

2、不支持collections.Set、collections.Map等类型。

3、不支持非buildin类型,如PixelMap、NativePointer、ArrayList等Native类型。

 

PersistenceV2

PersistenceV2是在应用UI启动时会被创建的单例,用于储存持久化的数据。不同于AppStorageV2它会将最新数据储存在设备磁盘上(持久化)。这意味着,应用退出再次启动后,依然能保存之前的数据。

支持应用的主线程内多个UIAbility实例间的状态共享。

注意:由于将数据存储在设备磁盘上,需要将数据进行序列化(数据转为某种固定的格式进行存储,例如全部转为二进制数据)后进行存储

connect

remove

keys

PersistenceV2类型上继承了AppStorageV2,在AppStorageV2的基础上多实现了save和notifyOnError方法,所以上面3个方法使用同AppStorageV2。

save

PersistenceV2.save<T>(keyOrType: string | TypeConstructorWithArgs<T>): void;

对于与PersistenceV2关联的@ObservedV2对象,该对象的@Trace属性的变化,会触发整个关联对象的自动持久化;非@Trace属性的变化则不会,这时需要通过该方法进行手动持久化。

save 说明
参数 keyOrType:需要手动持久化的key;如果指定的是type类型,key为type的name。
返回值 无。

入参字符串或者类名。传类名表示对类名对应字符串后的键值对进行持久化处理。 

notifyOnError

PersistenceV2.notifyOnError(callback: PersistenceErrorCallback | undefined): void;

将数据存入磁盘时,需要对数据进行序列化;当某个key序列化失败时,错误是不可预知的;可调用该接口捕获异常。

notifyOnError 说明
参数 callback:当序列化或者反序列化失败时,执行该回调;若传入undefined,取消该回调。
返回值 无。

 示例

注意:需要在真机环境运行,在预览环境会报错。

这里示例和AppStorageV2的示例效果类似。不同在于:

1.退出应用后再次打开应用p1的值不会重置,因为使用的PersistenceV2并被@Trace修饰了,会自动持久化数据。

2.p2的值需要点击Page1 save the key Sample按钮后才会被持久化。这时退出后再打开p2的值会同步。

Index.ets页面

import { Type } from '@kit.ArkUI';
import { PersistenceV2 } from '@kit.ArkUI';

// 数据中心
@ObservedV2
class SampleChild {
  @Trace p1: number = 0;
  p2: number = 10;
}

@ObservedV2
export class Sample {
  // 对于复杂对象需要@Type修饰,确保序列化成功
  @Type(SampleChild)
  @Trace f: SampleChild = new SampleChild();
}
// 接受序列化失败的回调
PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
});

@Entry
@ComponentV2
struct Page1 {
  // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
  // 对于需要换connect对象的prop属性,需要加@Local修饰(不建议对属性换connect的对象)
  @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!;
  pageStack: NavPathStack = new NavPathStack();

  build() {
    Navigation(this.pageStack) {
      Column() {
        Button('Go to page2')
          .onClick(() => {
            this.pageStack.pushPathByName('Page2', null);
          })

        Button('Page1 connect the key Sample')
          .onClick(() => {
            // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
            // 不建议对prop属性换connect的对象
            this.prop = PersistenceV2.connect(Sample, 'Sample', () => new Sample())!;
          })

        Button('Page1 remove the key Sample')
          .onClick(() => {
            // 从PersistenceV2中删除后,prop将不会再与key为Sample的值关联
            PersistenceV2.remove(Sample);
          })

        Button('Page1 save the key Sample')
          .onClick(() => {
            // 如果处于connect状态,持久化key为Sample的键值对
            PersistenceV2.save("Sample");
          })

        Text(`Page1 add 1 to prop.p1: ${this.prop.f.p1}`)
          .fontSize(30)
          .onClick(() => {
            this.prop.f.p1++;
          })

        Text(`Page1 add 1 to prop.p2: ${this.prop.f.p2}`)
          .fontSize(30)
          .onClick(() => {
            // 页面不刷新,但是p2的值改变了
            this.prop.f.p2++;
          })

        // 获取当前PersistenceV2里面的所有key
        Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`)
          .fontSize(30)
      }
    }
  }
}

Index2.ets 

import { PersistenceV2 } from '@kit.ArkUI';
import { Sample } from './Index';

@Builder
export function Page2Builder() {
  Page2()
}

@ComponentV2
struct Page2 {
  // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
  // 对于需要换connect对象的prop属性,需要加@Local修饰(不建议对属性换connect的对象)
  @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!;
  pathStack: NavPathStack = new NavPathStack();

  build() {
    NavDestination() {
      Column() {
        Button('Page2 connect the key Sample1')
          .onClick(() => {
            // 在PersistenceV2中创建一个key为Sample1的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联
            // 不建议对prop属性换connect的对象
            this.prop = PersistenceV2.connect(Sample, 'Sample1', () => new Sample())!;
          })

        Text(`Page2 add 1 to prop.p1: ${this.prop.f.p1}`)
          .fontSize(30)
          .onClick(() => {
            this.prop.f.p1++;
          })

        Text(`Page2 add 1 to prop.p2: ${this.prop.f.p2}`)
          .fontSize(30)
          .onClick(() => {
            // 页面不刷新,但是p2的值改变了;只有重新初始化才会改变
            this.prop.f.p2++;
          })

        // 获取当前PersistenceV2里面的所有key
        Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`)
          .fontSize(30)
      }
    }
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
    })
  }
}

 使用Navigation组件时,需要添加配置系统路由表文件,先在module.json5中添加:"routerMap": "$profile:route_map",表示使用src/main/resources/base/profile目录下的route_map.json文件作为路由表。故在该目录下创建文件route_map.json。

{
  "routerMap": [
    {
      "name": "Page2",
      "pageSourceFile": "src/main/ets/pages/Index2.ets",
      "buildFunction": "Page2Builder",
      "data": {
        "description" : "AppStorageV2 example"
      }
    }
  ]
}

使用限制

1、需要配合UI使用(UI线程),不能在其他线程使用,如不支持@Sendable。

2、不支持collections.Set、collections.Map等类型。

3、不支持非buildin类型,如PixelMap、NativePointer、ArrayList等Native类型。

4、单个key支持数据大小约8k,过大会导致持久化失败。

5、持久化的数据必须是class对象,不能是容器(如Array、Set、Map),不能是buildin的构造对象(如Date、Number)。

6、不支持循环引用的对象。

7、只有@Trace的数据改变会触发自动持久化,如V1状态变量、@Observed对象、普通数据的改变不会触发持久化。

8、不宜大量持久化数据,可能会导致页面卡顿。

@Type

在持久化数据时,为了实现序列化类时不丢失属性的复杂类型,可以使用@Type装饰器装饰类属性。

@Type装饰器 说明
装饰器参数 type:类型。
可装饰的类型 Object class以及Array、Date、Map、Set等内嵌类型。

class Sample {
  data: number = 0;
}
// Info会在PersistenceV2中使用。
//@Local prop: Info = PersistenceV2.connect(Info , () => new Info ())!;
@ObservedV2
class Info {
  @Type(Sample)
  @Trace sample: Sample = new Sample(); // 正确用法
}

 使用限制

1、只能用在@ObservedV2装饰的类中,不能用在自定义组件中。

2、不支持collections.Set、collections.Map等类型。

3、不支持非buildin类型,如PixelMap、NativePointer、ArrayList等Native类型。

4、不支持简单类型,如string、number、boolean等。

Logo

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

更多推荐