JHHK

欢迎来到我的个人网站
行者常至 为者常成

应用级状态管理

目录

LocalStorage 页面级UI状态存储

LocalStorage是ArkTS为构建页面级别状态变量提供存储的内存内的“数据库”。

应用程序可以创建多个LocalStorage实例,LocalStorage实例可以在页面内共享,也可以通过getSharedLocalStorage接口,实现跨页面、跨UIAbility实例共享。

组件树的根节点,即被@Entry装饰的@Component,可以被分配一个LocalStorage实例,此组件的所有子组件实例将自动获得对该LocalStorage实例的访问权限。

一、应用逻辑使用LocalStorage

// 创建新实例并使用给定对象初始化
let para: Record<string,number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para); 

// 获取值,propA只是一个普通变量,没有绑定
let propA: number | undefined = storage.get('PropA') // propA == 47

// link1和link2为双向绑定的变量
let link1: SubscribedAbstractProperty<number> = storage.link('PropA'); // link1.get() == 47
let link2: SubscribedAbstractProperty<number> = storage.link('PropA'); // link2.get() == 47

// prop为单向绑定的变量
let prop: SubscribedAbstractProperty<number> = storage.prop('PropA'); // prop.get() == 47

//修改后产生的效果
link1.set(48); // two-way sync: link1.get() == link2.get() == prop.get() == 48
prop.set(1); // one-way sync: prop.get() == 1; but link1.get() == link2.get() == 48
link1.set(49); // two-way sync: link1.get() == link2.get() == prop.get() == 49

二、从UI内部使用LocalStorage

// 创建新实例并使用给定对象初始化
let para: Record<string, number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para);


// 绑定到页面的根节点    
// 使LocalStorage可从@Component组件访问
@Entry(storage) 
@Component
struct CompA {
     // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
     @LocalStorageLink('PropA') parentLinkNumber: number = 1; // 1 是默认值,当LocalStorage中没有PropA时使用
     
     build () {
        Column () {
            // @Component子组件自动获得对CompA LocalStorage实例的访问权限。
            child()
        }
     }
}

@Component
struct Child {
     // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
     @LocalStorageLink('PropA') childLinkNumber: number = 1;
    build () {
        ...
    }
 }

@LocalStorageProp的用法与 @LocalStorageLink 一样,只不过是单向绑定的

三、将LocalStorage实例从UIAbility共享到一个或多个视图

如果希望其在多个视图中共享,可以在所属UIAbility中创建LocalStorage实例,并调用windowStage.loadContent

export default class EntryAbility extends UIAbility {
    storage: LocalStorage = new LocalStorage({ 'PropA': 47 });

    onWindowStageCreate(windowStage: window.WindowStage) {
        windowStage.loadContent('pages/Index', this.storage);
    }
}

可以在多个视图中共享

// index.ets

// 通过getShared接口获取stage共享的LocalStorage实例
let storage = LocalStorage.getShared()

@Entry(storage)
@Component
struct Index {
  @LocalStorageLink('PropA') propA: number = 1;
  
  build () {
    Column () {
        Text(`${this.propA}`)
    }
  }
}
// page.ets

// 通过getShared接口获取stage共享的LocalStorage实例
let storage = LocalStorage.getShared()

@Entry(storage)
@Component
struct Page {
  @LocalStorageLink('PropA') propA: number = 1;
  
  build () {
    Row () {
       Text(`${this.propA}`)
    }
  }
}

对于开发者更建议使用这个方式来构建LocalStorage的实例,并且在创建LocalStorage实例的时候就写入默认值,因为默认值可以作为运行异常的备份,也可以用作页面的单元测试。

四、其它用法

1、自定义组件接收LocalStorage实例
除了根节点可通过@Entry来接收LocalStorage实例,自定义组件(子节点)也可以通过构造参数来传递LocalStorage实例。

2、Navigation组件和LocalStorage联合使用

AppStorage 应用级UI状态存储

AppStorage是在应用启动时创建的单例,用于提供应用状态数据的中心存储。这些状态数据在应用级别可访问。AppStorage在应用运行过程中保留其属性。

一、从应用逻辑使用AppStorage

AppStorage是单例,它的所有API都是静态的,使用方法类似于LocalStorage中对应的非静态方法。

AppStorage.setOrCreate('PropA', 47);

// 普通变量
let propA: number | undefined = AppStorage.get('PropA') // propA in AppStorage == 47

// 双向绑定状态变量
let link1: SubscribedAbstractProperty<number> = AppStorage.link('PropA'); // link1.get() == 47
let link2: SubscribedAbstractProperty<number> = AppStorage.link('PropA'); // link2.get() == 47

// 单向绑定状态变量
let prop: SubscribedAbstractProperty<number> = AppStorage.prop('PropA'); // prop.get() == 47

// 修改值
link1.set(48); // two-way sync: link1.get() == link2.get() == prop.get() == 48
prop.set(1); // one-way sync: prop.get() == 1; but link1.get() == link2.get() == 48
link1.set(49); // two-way sync: link1.get() == link2.get() == prop.get() == 49

// 获取值
AppStorage.get<number>('PropA') // == 49
link1.get() // == 49
link2.get() // == 49
prop.get() // == 49

二、从UI内部使用AppStorage


AppStorage.setOrCreate('PropA', 47);

@Entry()
@Component
struct CompA {
  @StorageLink('PropA') storageLink: number = 1;
  
  build() {
    Column() { 
       Text(`From AppStorage ${this.storageLink}`)
    }
  }
 }

PersistentStorage:持久化存储UI状态

前面介绍的LocalStorage和AppStorage都是运行时的内存
但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistentStorage。

PersistentStorage和UI实例相关联,持久化操作需要在UI实例初始化成功后(即loadContent传入的回调被调用时)才可以被调用,早于该时机调用会导致持久化失败。


// EntryAbility.ets
onWindowStageCreate(windowStage: window.WindowStage): void {
  windowStage.loadContent('pages/Index', (err) => {
    if (err.code) { return; }
    PersistentStorage.persistProp('aProp', 47);
  });
}

新应用安装后首次启动运行:
a.调用persistProp初始化PersistentStorage,首先查询在PersistentStorage本地文件中是否存在“aProp”,查询结果为不存在,因为应用是第一次安装。
b.接着查询属性“aProp”在AppStorage中是否存在,依旧不存在。
c.在AppStorge中创建名为“aProp”的number类型属性,属性初始值是定义的默认值47。
d.PersistentStorage将属性“aProp”和值47写入磁盘,AppStorage中“aProp”对应的值和其后续的更改将被持久化。
e.在Index组件中创建状态变量@StorageLink(‘aProp’) aProp,和AppStorage中“aProp”双向绑定,在创建的过程中会在AppStorage中查找,成功找到“aProp”,所以使用其在AppStorage找到的值47。

触发点击事件后:
a.状态变量@StorageLink(‘aProp’) aProp改变,触发Text组件重新刷新。
b.@StorageLink装饰的变量是和AppStorage中建立双向同步的,所以@StorageLink(‘aProp’) aProp的变化会被同步回AppStorage中。
c.AppStorage中“aProp”属性的改变会同步到所有绑定该“aProp”的单向或者双向变量,在本示例中没有其他的绑定“aProp”的变量。
d.因为“aProp”对应的属性已经被持久化,所以在AppStorage中“aProp”的改变会触发PersistentStorage,将新的改变写入本地磁盘。

后续启动应用:
a.执行PersistentStorage.persistProp(‘aProp’, 47),在首先查询在PersistentStorage本地文件查询“aProp”属性,成功查询到。
b.将在PersistentStorage查询到的值写入AppStorage中。
c.在Index组件里,@StorageLink绑定的“aProp”为PersistentStorage写入AppStorage中的值,即为上一次退出应用存入的值。

总结

PersistentStorage.persistProp(‘aProp’, 47); 告知将属性aProp持久化,后续aProp属性值的修改和获取都通过AppStorge的使用方式来进行

在AppStorage获取对应属性:

AppStorage.get<number>('aProp'); // returns 47

或在组件内部定义:

@StorageLink('aProp') aProp: number = 48;

Environment:设备环境查询

一、介绍

开发者如果需要应用程序运行的设备的环境参数,以此来作出不同的场景判断,比如多语言,暗黑模式等,需要用到Environment设备环境查询。

设备环境到Component的更新链:Environment –> AppStorage –>Component。

@StorageProp关联的环境参数可以在本地更改,但不能同步回AppStorage中,因为应用对环境变量参数是不可写的,只能在Environment中查询。

二、使用

Environment和UIContext相关联,需要在UIContext明确的时候才可以调用。
可以通过在runScopedTask里明确上下文。如果没有在UIContext明确的地方调用,将导致无法查询到设备环境数据。

// EntryAbility.ets
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/Index');
    let window = windowStage.getMainWindow()
    window.then(window => {
      let uicontext = window.getUIContext()
      uicontext.runScopedTask(() => {
        // 使用Environment.envProp将设备运行的环境变量存入AppStorage中:
        Environment.envProp('languageCode', 'en');
      })
    })
  }
}

应用逻辑使用Environment

// 使用Environment.EnvProp将设备运行languageCode存入AppStorage中;
Environment.envProp('languageCode', 'en');
// 从AppStorage获取单向绑定的languageCode的变量
const lang: SubscribedAbstractProperty<string> = AppStorage.prop('languageCode');

if (lang.get() === 'zh') {
  console.info('你好');
} else {
  console.info('Hello!');
}

从UI中访问Environment参数

@Entry
@Component
struct Index {
  @StorageProp('languageCode') languageCode: string = 'en';

  build() {
    Row() {
      Column() {
        // 输出当前设备的languageCode
        Text(this.languageCode)
      }
    }
  }
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.