1# PersistenceV2: 持久化储存UI状态 2 3为了增强状态管理框架对持久化存储UI的能力,开发者可以使用PersistenceV2存储持久化的数据。AppStorageV2是运行时内存,但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistenceV2。 4 5PersistenceV2是应用程序中的可选单例对象。此对象的作用是持久化存储UI相关的数据,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。 6 7PersistenceV2提供状态变量持久化能力,开发者可以通过connect绑定同一个key,在状态变量变换和应用冷启动时,实现持久化能力。 8 9在阅读本文当前,建议提前阅读:[\@ComponentV2](./arkts-new-componentV2.md),[\@ObservedV2和\@Trace](./arkts-new-observedV2-and-trace.md),配合阅读:[PersistentV2-API文档](../reference/apis-arkui/js-apis-StateManagement.md#persistencev2) 10 11>**说明:** 12> 13>PersistenceV2从API version 12开始支持。 14> 15 16 17## 概述 18 19PersistenceV2是在应用UI启动时会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。数据通过唯一的键字符串值访问。不同于AppStorageV2,PersistenceV2还将最新数据储存在设备磁盘上(持久化)。这意味着,应用退出再次启动后,依然能保存选定的结果。 20 21对于与PersistenceV2关联的[\@ObservedV2](arkts-new-observedV2-and-trace.md)对象,该对象的[\@Trace](arkts-new-observedV2-and-trace.md)属性的变化,会触发**整个关联对象的自动持久化**;非[\@Trace](arkts-new-observedV2-and-trace.md)属性的变化则不会,如有必要,可调用PersistenceV2 API手动持久化。 22 23PersistenceV2可以和UI组件同步,且可以在应用业务逻辑中被访问。 24 25PersistenceV2支持应用的[主线程](../application-models/thread-model-stage.md)内多个UIAbility实例间的状态共享。 26 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 > keyOrDefaultCreater:指定的key,或者是默认数据的构造器;</br > defaultCreator:默认数据的构造器。 | 43| 返回值 | 创建或获取数据成功时,返回数据;否则返回undefined。 | 44 45>**说明:** 46> 47>1、若未指定key,使用第二个参数作为默认构造器;否则使用第三个参数作为默认构造器(第二个参数非法也使用第三个参数作为默认构造器); 48> 49>2、确保数据已经存储在PersistenceV2中,可省略默认构造器,获取存储的数据;否则必须指定默认构造器,不指定将导致应用异常; 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>删除PersistenceV2中不存在的key会报警告。 71 72### keys:返回所有PersistenceV2中的key 73 74```JavaScript 75static keys(): Array<string>; 76``` 77 78| keys | 说明 | 79| ------------ | ----------------------------------------------------- | 80| 参数 | 无。 | 81| 返回值 | 所有PersistenceV2中的key。 | 82 83 84### save:手动持久化数据 85 86```JavaScript 87static save<T>(keyOrType: string | TypeConstructorWithArgs<T>): void; 88``` 89 90| save | 说明 | 91| ------------ | ----------------------------------------------------- | 92| 参数 | keyOrType:需要手动持久化的key;如果指定的是type类型,key为type的name。 | 93| 返回值 | 无。 | 94 95>**说明:** 96> 97>由于非[\@Trace](arkts-new-observedV2-and-trace.md)的数据改变不会触发PersistenceV2的自动持久化,如有必要,可调用该接口持久化对应key的数据; 98> 99>手动持久化当前内存中不处于connect状态的key是无意义的。 100 101 102### notifyOnError:响应序列化或反序列化失败的回调 103 104```JavaScript 105static notifyOnError(callback: PersistenceErrorCallback | undefined): void; 106``` 107 108| notifyOnError| 说明 | 109| ------------ | ----------------------------------------------------- | 110| 参数 | callback:当序列化或者反序列化失败时,执行该回调;若传入undefined,取消该回调。| 111| 返回值 | 无。 | 112 113>**说明:** 114> 115>将数据存入磁盘时,需要对数据进行序列化;当某个key序列化失败时,错误是不可预知的;可调用该接口捕获异常。 116 117 118## 使用限制 119 1201、需要配合UI使用(UI线程),不能在其他线程使用,如不支持@Sendable; 121 1222、不支持collections.Set、collections.Map等类型; 123 1243、不支持非buildin类型,如PixelMap、NativePointer、ArrayList等Native类型; 125 1264、单个key支持数据大小约8k,过大会导致持久化失败; 127 1285、持久化的数据必须是class对象,不能是容器(如Array、Set、Map),不能是buildin的构造对象(如Date、Number); 129 1306、不支持循环引用的对象; 131 1327、只有[\@Trace](arkts-new-observedV2-and-trace.md)的数据改变会触发自动持久化,如V1状态变量、[\@Observed](arkts-observed-and-objectlink.md)对象、普通数据的改变不会触发持久化; 133 1348、不宜大量持久化数据,可能会导致页面卡顿。 135 136## 使用场景 137 138### 在两个页面之间存储数据 139 140页面1 141```ts 142import { PersistenceV2 } from '@kit.ArkUI'; 143import { Sample } from '../Sample'; 144 145// 接受序列化失败的回调 146PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => { 147 console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`); 148}); 149 150@Entry 151@ComponentV2 152struct Page1 { 153 // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联 154 // 对于需要换connect对象的prop属性,需要加@Local修饰(不建议对属性换connect的对象) 155 @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!; 156 pageStack: NavPathStack = new NavPathStack(); 157 158 build() { 159 Navigation(this.pageStack) { 160 Column() { 161 Button('Go to page2') 162 .onClick(() => { 163 this.pageStack.pushPathByName('Page2', null); 164 }) 165 166 Button('Page1 connect the key Sample') 167 .onClick(() => { 168 // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联 169 // 不建议对prop属性换connect的对象 170 this.prop = PersistenceV2.connect(Sample, 'Sample', () => new Sample())!; 171 }) 172 173 Button('Page1 remove the key Sample') 174 .onClick(() => { 175 // 从PersistenceV2中删除后,prop将不会再与key为Sample的值关联 176 PersistenceV2.remove(Sample); 177 }) 178 179 Button('Page1 save the key Sample') 180 .onClick(() => { 181 // 如果处于connect状态,持久化key为Sample的键值对 182 PersistenceV2.save(Sample); 183 }) 184 185 Text(`Page1 add 1 to prop.p1: ${this.prop.f.p1}`) 186 .fontSize(30) 187 .onClick(() => { 188 this.prop.f.p1++; 189 }) 190 191 Text(`Page1 add 1 to prop.p2: ${this.prop.f.p2}`) 192 .fontSize(30) 193 .onClick(() => { 194 // 页面不刷新,但是p2的值改变了 195 this.prop.f.p2++; 196 }) 197 198 // 获取当前PersistenceV2里面的所有key 199 Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`) 200 .fontSize(30) 201 } 202 } 203 } 204} 205``` 206 207页面2 208```ts 209import { PersistenceV2 } from '@kit.ArkUI'; 210import { Sample } from '../Sample'; 211 212@Builder 213export function Page2Builder() { 214 Page2() 215} 216 217@ComponentV2 218struct Page2 { 219 // 在PersistenceV2中创建一个key为Sample的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联 220 // 对于需要换connect对象的prop属性,需要加@Local修饰(不建议对属性换connect的对象) 221 @Local prop: Sample = PersistenceV2.connect(Sample, () => new Sample())!; 222 pathStack: NavPathStack = new NavPathStack(); 223 224 build() { 225 NavDestination() { 226 Column() { 227 Button('Page2 connect the key Sample1') 228 .onClick(() => { 229 // 在PersistenceV2中创建一个key为Sample1的键值对(如果存在,则返回PersistenceV2中的数据),并且和prop关联 230 // 不建议对prop属性换connect的对象 231 this.prop = PersistenceV2.connect(Sample, 'Sample1', () => new Sample())!; 232 }) 233 234 Text(`Page2 add 1 to prop.p1: ${this.prop.f.p1}`) 235 .fontSize(30) 236 .onClick(() => { 237 this.prop.f.p1++; 238 }) 239 240 Text(`Page2 add 1 to prop.p2: ${this.prop.f.p2}`) 241 .fontSize(30) 242 .onClick(() => { 243 // 页面不刷新,但是p2的值改变了;只有重新初始化才会改变 244 this.prop.f.p2++; 245 }) 246 247 // 获取当前PersistenceV2里面的所有key 248 Text(`all keys in PersistenceV2: ${PersistenceV2.keys()}`) 249 .fontSize(30) 250 } 251 } 252 .onReady((context: NavDestinationContext) => { 253 this.pathStack = context.pathStack; 254 }) 255 } 256} 257``` 258使用Navigation时,需要添加配置系统路由表文件src/main/resources/base/profile/route_map.json,并替换pageSourceFile为Page2页面的路径,并且在module.json5中添加:"routerMap": "$profile:route_map"。 259```json 260{ 261 "routerMap": [ 262 { 263 "name": "Page2", 264 "pageSourceFile": "src/main/ets/pages/Page2.ets", 265 "buildFunction": "Page2Builder", 266 "data": { 267 "description" : "AppStorageV2 example" 268 } 269 } 270 ] 271} 272``` 273 274数据页面 275```ts 276import { Type } from '@kit.ArkUI'; 277 278// 数据中心 279@ObservedV2 280class SampleChild { 281 @Trace p1: number = 0; 282 p2: number = 10; 283} 284 285@ObservedV2 286export class Sample { 287 // 对于复杂对象需要@Type修饰,确保序列化成功 288 @Type(SampleChild) 289 @Trace f: SampleChild = new SampleChild(); 290} 291``` 292 293