• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# AppStorageV2: 应用全局UI状态存储
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @zzq212050299-->
5<!--Designer: @s10021109-->
6<!--Tester: @TerryTsao-->
7<!--Adviser: @zhang_yixin13-->
8
9为了增强状态管理框架对应用全局UI状态变量存储的能力,开发者可以使用AppStorageV2存储应用全局UI状态变量数据。
10
11AppStorageV2是提供状态变量在应用级全局共享的能力,开发者可以通过connect绑定同一个key,进行跨ability的数据共享。
12
13在阅读本文档前,建议提前阅读:[\@ComponentV2](./arkts-new-componentV2.md),[\@ObservedV2和\@Trace](./arkts-new-observedV2-and-trace.md),配合阅读:[AppStorageV2-API文档](../../reference/apis-arkui/js-apis-StateManagement.md#appstoragev2)。
14
15>**说明:**
16>
17>AppStorageV2从API version 12开始支持。
18>
19
20## 概述
21
22AppStorageV2是在应用UI启动时会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。AppStorageV2将在应用运行过程保留其数据。数据通过唯一的键字符串值访问。需要注意的是,AppStorage与AppStorageV2之间的数据互不共享。
23
24AppStorageV2可以修改connect的返回值,实现与UI组件的同步。
25
26AppStorageV2支持应用的[主线程](../../application-models/thread-model-stage.md)内多个UIAbility实例间的状态共享。
27
28## 使用说明
29
30### connect:创建或获取存储的数据
31
32```JavaScript
33static connect<T extends object>(
34    type: TypeConstructorWithArgs<T>,
35    keyOrDefaultCreator?: string | StorageDefaultCreator<T>,
36    defaultCreator?: StorageDefaultCreator<T>
37): T | undefined;
38```
39
40| connect      | 说明                                                  |
41| ------------ | ----------------------------------------------------- |
42| 参数         | type:指定的类型,若未指定key,则使用type的name作为key;</br>keyOrDefaultCreator:指定的key,或者是默认数据的构造器;</br>defaultCreator:默认数据的构造器。                                          |
43| 返回值       | 创建或获取数据成功时,返回数据;否则返回undefined。 |
44
45>**说明:**
46>
47>1、若未指定key,使用第二个参数作为默认构造器;否则使用第三个参数作为默认构造器(第二个参数非法也使用第三个参数作为默认构造器)。
48>
49>2、确保数据已经存储在AppStorageV2中,可省略默认构造器,获取存储的数据;否则必须指定默认构造器,不指定将导致应用异常。
50>
51>3、同一个key,connect不同类型的数据会导致应用异常,应用需要确保类型匹配。
52>
53>4、key建议使用有意义的值,可由字母、数字、下划线组成,长度不超过255,使用非法字符或空字符的行为是未定义的。
54>
55>5、关联[\@Observed](arkts-observed-and-objectlink.md)对象时,由于该类型的name属性未定义,需要指定key或者自定义name属性。
56
57### remove:删除指定key的存储数据
58
59```JavaScript
60static remove<T>(keyOrType: string | TypeConstructorWithArgs<T>): void;
61```
62
63| remove       | 说明                                                  |
64| ------------ | ----------------------------------------------------- |
65| 参数         | keyOrType:需要删除的key;如果指定的是type类型,删除的key为type的name。                                          |
66| 返回值       | 无。 |
67
68>**说明:**
69>
70>删除AppStorageV2中不存在的key会报警告。
71
72### keys:返回所有AppStorageV2中的key
73
74```JavaScript
75static keys(): Array<string>;
76```
77
78| keys         | 说明                                                  |
79| ------------ | ----------------------------------------------------- |
80| 参数         | 无。                                         |
81| 返回值       | 所有AppStorageV2中的key。 |
82
83
84## 使用限制
85
861、只支持class类型。
87
882、需要配合UI使用(UI线程),不能在其他线程使用,如不支持@Sendable。
89
903、不支持collections.Setcollections.Map等类型。
91
924、不支持非built-in类型,如PixelMap、NativePointer、ArrayList等Native类型。
93
945、不支持存储基本类型,如string、number、boolean等。注意:不支持存储基本类型意味着[connect](#connect创建或获取存储的数据)接口传入的类型不能是基本类型,但connect传入的class中可以包含基本类型。
95
96## 使用场景
97
98### 使用AppStorageV2
99
100AppStorageV2使用connect接口即可实现对AppStorageV2中数据的修改和同步,如果修改的数据被@Trace装饰,该数据的修改会同步更新UI。需要注意的是,使用remove接口只会将数据从AppStorageV2中删除,不影响组件中已创建的数据,详见以下示例代码:
101
102```ts
103import { AppStorageV2 } from '@kit.ArkUI';
104
105@ObservedV2
106class Message {
107  @Trace userID: number;
108  userName: string;
109
110  constructor(userID?: number, userName?: string) {
111    this.userID = userID ?? 1;
112    this.userName = userName ?? 'Jack';
113  }
114}
115
116@Entry
117@ComponentV2
118struct Index {
119  // 使用connect在AppStorageV2中创建一个key为Message的对象
120  // 修改connect的返回值即可同步回AppStorageV2
121  @Local message: Message = AppStorageV2.connect<Message>(Message, () => new Message())!;
122
123  build() {
124    Column() {
125      // 修改@Trace装饰的类属性,UI能同步刷新
126      Button(`Index userID: ${this.message.userID}`)
127        .onClick(() => {
128          this.message.userID += 1;
129        })
130      // 修改非@Trace装饰的类属性,UI不会同步刷新,但修改的类属性已同步回AppStorageV2
131      Button(`Index userName: ${this.message.userName}`)
132        .onClick(() => {
133          this.message.userName += 'suf';
134        })
135      // remove key Message, 会从AppStorageV2中删除key为Message的对象
136      // remove之后,修改父组件的userId,子组件能同步变化,因为remove只是从AppStorageV2删除,不会影响组件中已存在的数据
137      Button('remove key: Message')
138        .onClick(() => {
139          AppStorageV2.remove<Message>(Message);
140        })
141      // connect key Message, 会从AppStorageV2中添加key为Message的对象
142      // remove之后,重新添加,修改父子组件的userID,可以发现数据已经不同步,子组件重新connect之后,数据一致
143      Button('connect key: Message')
144        .onClick(() => {
145          this.message = AppStorageV2.connect<Message>(Message, () => new Message(5, 'Rose'))!;
146        })
147      Divider()
148      Child()
149    }
150    .width('100%')
151    .height('100%')
152  }
153}
154
155@ComponentV2
156struct Child {
157  // 使用connect在AppStorageV2中取出一个key为Message的对象,已在父组件中创建
158  @Local message: Message = AppStorageV2.connect<Message>(Message, () => new Message())!;
159  @Local name: string = this.message.userName;
160
161  build() {
162    Column() {
163      // 修改@Trace装饰的类属性,UI同步刷新,父组件能感知该变化
164      Button(`Child userID: ${this.message.userID}`)
165        .onClick(() => {
166          this.message.userID += 5;
167        })
168      // 修改父组件中的userName属性,点击name可以同步父组件的类属性修改
169      Button(`Child name: ${this.name}`)
170        .onClick(() => {
171          this.name = this.message.userName;
172        })
173      // remove key Message, 会从AppStorageV2中删除key为Message的对象
174      Button('remove key: Message')
175        .onClick(() => {
176          AppStorageV2.remove<Message>(Message);
177        })
178      // connect key Message, 会从AppStorageV2中添加key为Message的对象
179      Button('connect key: Message')
180        .onClick(() => {
181          this.message = AppStorageV2.connect<Message>(Message, () => new Message(10, 'Lucy'))!;
182        })
183    }
184    .width('100%')
185    .height('100%')
186  }
187}
188```
189
190### 在两个页面之间存储数据
191
192数据页面
193```ts
194// 数据中心
195// Sample.ets
196@ObservedV2
197export class Sample {
198  @Trace p1: number = 0;
199  p2: number = 10;
200}
201```
202
203页面1
204```ts
205// Page1.ets
206import { AppStorageV2 } from '@kit.ArkUI';
207import { Sample } from '../Sample';
208
209@Entry
210@ComponentV2
211struct Page1 {
212  // 在AppStorageV2中创建一个key为Sample的键值对(如果存在,则返回AppStorageV2中的数据),并且和prop关联
213  @Local prop: Sample = AppStorageV2.connect(Sample, () => new Sample())!;
214  pageStack: NavPathStack = new NavPathStack();
215
216  build() {
217    Navigation(this.pageStack) {
218      Column() {
219        Button('Go to page2')
220          .onClick(() => {
221            this.pageStack.pushPathByName('Page2', null);
222          })
223
224        Button('Page1 connect the key Sample')
225          .onClick(() => {
226            // 在AppStorageV2中创建一个key为Sample的键值对(如果存在,则返回AppStorageV2中的数据),并且和prop关联
227            this.prop = AppStorageV2.connect(Sample, 'Sample', () => new Sample())!;
228          })
229
230        Button('Page1 remove the key Sample')
231          .onClick(() => {
232            // 从AppStorageV2中删除后,prop将不会再与key为Sample的值关联
233            AppStorageV2.remove(Sample);
234          })
235
236        Text(`Page1 add 1 to prop.p1: ${this.prop.p1}`)
237          .fontSize(30)
238          .onClick(() => {
239            this.prop.p1++;
240          })
241
242        Text(`Page1 add 1 to prop.p2: ${this.prop.p2}`)
243          .fontSize(30)
244          .onClick(() => {
245            // 页面不刷新,但是p2的值改变了
246            this.prop.p2++;
247          })
248
249        // 获取当前AppStorageV2里面的所有key
250        Text(`all keys in AppStorage: ${AppStorageV2.keys()}`)
251          .fontSize(30)
252      }
253    }
254  }
255}
256```
257
258页面2
259```ts
260// Page2.ets
261import { AppStorageV2 } from '@kit.ArkUI';
262import { Sample } from '../Sample';
263
264@Builder
265export function Page2Builder() {
266  Page2()
267}
268
269@ComponentV2
270struct Page2 {
271  // 在AppStorageV2中创建一个key为Sample的键值对(如果存在,则返回AppStorageV2中的数据),并且和prop关联
272  @Local prop: Sample = AppStorageV2.connect(Sample, () => new Sample())!;
273  pathStack: NavPathStack = new NavPathStack();
274
275  build() {
276    NavDestination() {
277      Column() {
278        Button('Page2 connect the key Sample1')
279          .onClick(() => {
280            // 在AppStorageV2中创建一个key为Sample1的键值对(如果存在,则返回AppStorageV2中的数据),并且和prop关联
281            this.prop = AppStorageV2.connect(Sample, 'Sample1', () => new Sample())!;
282          })
283
284        Text(`Page2 add 1 to prop.p1: ${this.prop.p1}`)
285          .fontSize(30)
286          .onClick(() => {
287            this.prop.p1++;
288          })
289
290        Text(`Page2 add 1 to prop.p2: ${this.prop.p2}`)
291          .fontSize(30)
292          .onClick(() => {
293            // 页面不刷新,但是p2的值改变了;只有重新初始化才会改变
294            this.prop.p2++;
295          })
296
297        // 获取当前AppStorageV2里面的所有key
298        Text(`all keys in AppStorage: ${AppStorageV2.keys()}`)
299          .fontSize(30)
300      }
301    }
302    .onReady((context: NavDestinationContext) => {
303      this.pathStack = context.pathStack;
304    })
305  }
306}
307```
308使用Navigation时,需要添加配置系统路由表文件src/main/resources/base/profile/route_map.json,并替换pageSourceFile为Page2页面的路径,并且在module.json5中添加:"routerMap": "$profile:route_map"。
309```json
310{
311  "routerMap": [
312    {
313      "name": "Page2",
314      "pageSourceFile": "src/main/ets/pages/Page2.ets",
315      "buildFunction": "Page2Builder",
316      "data": {
317        "description" : "AppStorageV2 example"
318      }
319    }
320  ]
321}
322```
323