鸿蒙应用开发之持久化存储解析:PersistentStorage / PersistenceV2 / preferences 选型与实战
文章目录
一、什么是持久化存储
在鸿蒙(HarmonyOS)应用开发中,持久化存储指的是将 UI 相关的状态数据保存到磁盘,使得应用重启后这些数据能够自动恢复。持久化存储与普通文件存储不同,它专为状态管理设计,与组件生命周期、装饰器深度集成,开发者无需手动读写文件即可实现状态保活。
持久化方案分为两代:V1 版本的 PersistentStorage(API 9 引入)和 V2 版本的 PersistenceV2(API 12 引入),后者在性能、容量和开发体验上全面升级。
二、V1 持久化方案:PersistentStorage
2.1 核心概念
PersistentStorage 是一个应用全局单例,负责持久化 UI 状态数据。它通过 persistProp 方法设置初始值并绑定键名,UI 组件通过 @StorageLink 或 @StorageProp 装饰器与该键建立连接。
- 数据容量:单 key 不超过 2KB。
- 操作方式:同步磁盘写入,可能在主线程产生卡顿。
- 路径隔离:仅当前 module 可见。
2.2 完整使用示例
// 1. 在应用入口或必要处持久化初始值
PersistentStorage.persistProp('userScore', 0);
@Entry
@Component
struct ScoreBoard {
// @StorageLink 双向绑定:UI 修改会自动写回磁盘
@StorageLink('userScore') score: number = 0;
build() {
Column({ space: 16 }) {
Text(`当前积分:${this.score}`)
.fontSize(24)
Button('增加积分')
.onClick(() => {
this.score += 10;
})
Button('重置积分')
.onClick(() => {
this.score = 0;
})
}
.padding(20)
.width('100%')
.height('100%')
}
}
运行效果:

运行说明:修改积分并重启应用后,userScore 的值为修改后的值,而不是初始值。
必须在 UI 实例初始化成功后(即 loadContent 回调中)调用 这一重要约束。根据官方文档,早于该时机调用会导致持久化失败。
2.3 PersistentStorage 的适用场景
- 保存少量全局配置项(如用户偏好、主题选择)。
- 简单游戏积分、上次浏览位置等轻量数据。
- 兼容旧项目(API 9–12 范围内)。
三、V2 持久化方案:PersistenceV2
3.1 核心概念
PersistenceV2 是专为状态管理 V2 设计的新一代持久化方案,它支持:
- 通过
PersistenceV2.connect(type: TypeConstructorWithArgs<T>, keyOrDefaultCreator?, defaultCreator?)连接一个持久化键。 - 通过
PersistenceV2.globalConnect(key, defaultValue)跨 module 共享(应用级)。 PersistenceV2在 API 12 ~ API 22 版本中存在明确限制:不支持直接持久化基本数据类型(如 string、number、boolean),也不支持容器类型(如 Array、Map、Set)以及内置构造对象(如 Date、Number)。从 API 23 开始,支持集合类型(Array、Map、Set、Date)、@Sendable对象、循环引用对象,以及单个 key 超过 8KB 的大数据量。
3.2 基本使用
import { PersistenceV2 } from '@kit.ArkUI';
@ObservedV2
class ThemeConfig {
@Trace themeMode: string = 'light';
}
@Entry
@ComponentV2
struct SettingsPage {
@Local themeConfig: ThemeConfig = PersistenceV2.connect(ThemeConfig, () => new ThemeConfig())!;
build() {
Column({ space: 20 }) {
Text(`当前主题:${this.themeConfig.themeMode == 'light' ? '浅色模式' : '深色模式'}`)
.fontSize(20)
.fontColor(Color.Red)
Button('切换为深色模式')
.onClick(() => {
this.themeConfig.themeMode = 'dark';
})
Button('切换为浅色模式')
.onClick(() => {
this.themeConfig.themeMode = 'light';
})
}
.padding(20)
.width('100%')
.height('100%')
.backgroundColor(this.themeConfig.themeMode == 'light' ? Color.White : Color.Black)
}
}
运行效果:

3.3 高级用法:集合类型持久化
import { PersistenceV2 } from '@kit.ArkUI';
@Entry
@ComponentV2
struct FavoritesPage {
@Local
favoriteIds: number[] = (PersistenceV2.globalConnect({
type: Array<number>,
defaultCreator: () => []
}) ?? []);
build() {
Column({ space: 12 }) {
Text(`已收藏的文章数:${this.favoriteIds.length}`)
.fontSize(20)
Button('添加收藏 (ID=1001)')
.width("80%")
.onClick(() => {
this.favoriteIds = [...this.favoriteIds, 1001];
})
Button('清空收藏')
.width("80%")
.onClick(() => {
this.favoriteIds = [];
})
}
.padding(20)
.width("100%")
}
}
运行效果:

上面的代码存在问题,虽然点击添加收藏按钮,收藏数量会实时变化,但是重新启动应用后,已收藏的文章数量清零了。这是因为 this.favoriteIds = [...this.favoriteIds, 1001] 会创建一个全新的数组对象,切断了与 PersistenceV2 的连接。
PersistenceV2.globalConnect() 返回的对象是框架代理后的可观察对象。当咱们用 = 整体赋值一个新数组时:
✅ @Local 检测到引用变化 → UI 刷新
❌ 新数组与 PersistenceV2 失去连接 → 不会触发自动持久化
❌ 应用重启后,PersistenceV2 读到的仍是旧数据
怎么解决这个问题呢?咱们可以利用 @ObservedV2 + @Trace 装饰器,让数组作为 class 的 属性被观测,而不是直接作为顶层类型持久化。修改后的代码如下:
import { PersistenceV2 } from '@kit.ArkUI';
// 1. 用 class 包装数组,@Trace 追踪变化
@ObservedV2
class FavoriteIds {
@Trace
ids: number[] = [];
}
@Entry
@ComponentV2
struct FavoritesPage {
// 2. globalConnect 连接 class 实例
@Local
favoriteData: FavoriteIds = (PersistenceV2.globalConnect({
type: FavoriteIds,
defaultCreator: () => new FavoriteIds()
}) ?? new FavoriteIds());
build() {
Column({ space: 12 }) {
Text(`已收藏的文章数:${this.favoriteData.ids.length}`)
.fontSize(20)
Button('添加收藏 (ID=1001)')
.width('80%')
.onClick(() => {
// 3. 修改 @Trace 属性的值 → 触发自动持久化
this.favoriteData.ids = [...this.favoriteData.ids, 1001];
})
Button('清空收藏')
.width('80%')
.onClick(() => {
this.favoriteData.ids = [];
})
}
.padding(20)
.width("100%")
}
}
修改后的数据流向图:
应用重启启动
↓
PersistenceV2.globalConnect({ type: FavoriteIds })
↓ 从磁盘读取持久化数据
favoriteData = 框架代理后的 FavoriteIds 实例
↓
用户点击按钮 → this.favoriteData.ids = [...newArray]
↓
@Trace 检测到 ids 属性变化
↓
┌─────────────────────┐
│ ① → UI 自动刷新 ✅ │
│ ② → 自动写入磁盘 ✅ │
└─────────────────────┘
3.4 PersistenceV2 的优势
- 丰富的数据类型:数组、Map、Set、Date 原生支持,无需手动序列化。
- 大数据支持:单 key 可存储超过 8KB(API 23+)。
- 全局共享:通过
globalConnect可在不同 HAP/Module 间共享同一持久化数据。 - 自管理序列化:状态管理引擎自动完成序列化与反序列化。
四、preferences
除了 PersistentStorage 和 PersistenceV2,鸿蒙还提供了 preferences(首选项)模块,用于轻量级键值对数据的持久化存储。它属于 @kit.ArkData 套件,适合存储用户配置、设置项等不要求与 UI 状态管理强绑定的数据。
4.1 preferences 核心特点
- 轻量级:以键值对形式存储,适合存储简单的用户配置数据。
- 异步接口:所有操作(读取、写入、刷新)均返回 Promise,不会阻塞 UI 线程。
- 手动序列化:与状态管理装饰器解耦,开发者需自行读取并赋值给状态变量。
- 文件级隔离:不同的 preferences 文件(通过名称区分)可以管理不同类别的配置。
4.2 使用示例
以下示例演示如何使用 preferences 实现主题切换功能,将用户选择的主题模式持久化到本地,并在应用启动时恢复:
// 导入 preferences 模块,用于轻量级键值对数据持久化存储(如用户配置)
import { preferences } from '@kit.ArkData';
// 导入 AbilityKit 中的 common 模块,用于获取 UIAbilityContext,以便访问应用上下文
import { common } from '@kit.AbilityKit';
@Entry
@ComponentV2
struct Index {
@Local themeMode: string = 'light';
// 获取当前组件的 UIAbility 上下文对象
// 通过 getUIContext().getHostContext() 获取宿主 Ability 的上下文
// as common.UIAbilityContext 进行类型断言,使其具备操作应用数据的能力
private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
async aboutToAppear() {
// 获取(或创建)名为 'app_settings' 的首选项存储实例
const store = await preferences.getPreferences(this.context, 'app_settings');
// 从存储中读取 'themeMode' 键对应的值,若不存在则使用默认值 'light'
// get() 返回 Promise<ValueType>,必须用 await 获取实际值
this.themeMode = await store.get('themeMode', 'light') as string;
}
// switchTheme 方法:切换主题模式,并将新值持久化到本地存储
// @param newMode - 新的主题模式字符串('light' 或 'dark')
async switchTheme(newMode: string) {
// 获取首选项存储实例
const store = await preferences.getPreferences(this.context, 'app_settings');
// 将新的主题模式写入存储(键为 'themeMode',值为 newMode)
await store.put('themeMode', newMode);
// 手动刷新(flush)数据到磁盘,确保数据真正持久化保存
await store.flush();
// 更新组件内部的 themeMode 状态变量,触发 UI 刷新
this.themeMode = newMode;
}
build() {
Column({ space: 20 }) {
Text(`当前主题:${this.themeMode == 'light' ? '浅色模式' : '深色模式'}`)
.fontSize(20)
.fontColor(Color.Red)
Button('切换为深色模式')
.onClick(() => {
this.switchTheme('dark')
})
Button('切换为浅色模式')
.onClick(() => {
this.switchTheme('light')
})
}
.padding(20)
.width('100%')
.height('100%')
.backgroundColor(this.themeMode == 'light' ? Color.White : Color.Black)
}
}
运行效果:

4.3 preferences 与 PersistenceV2 的对比
| 维度 | preferences | PersistenceV2 |
|---|---|---|
| 设计定位 | 轻量级键值对存储 | 状态管理 V2 的内置持久化引擎 |
| 与状态管理集成 | 手动读写 | 自动序列化/反序列化,与 @StorageProp 集成 |
| 数据类型 | 基本类型(string、number、boolean) | 对象、集合、Sendable、循环引用(API 23+) |
| 容量 | 适合少量配置项 | 支持大数据量(API 23+ 单 key >8KB) |
| 跨 Module | 需自行管理文件路径 | 内置 module 级 / 应用级 connect |
4.4 使用建议
- 偏好设置/用户配置:优先使用
preferences,它更轻量,不需要引入状态管理 V2 的特性。 - UI 状态持久化:当需要持久化与 UI 绑定的数据(如列表项选中状态、表单输入)时,推荐使用
PersistenceV2以简化代码。 - 混合使用:两个方案不冲突,可以在同一应用中共存——用
preferences存配置,用PersistenceV2存 UI 状态。
五、PersistenceV2 VS PersistentStorage 选型建议与迁移指南
5.1 如何选择
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 新项目(API 12+) | PersistenceV2 | 性能好、功能全、开发体验佳 |
| 旧项目(API 9–11) | PersistentStorage | 兼容已有代码,零迁移成本 |
| 需要跨 Module 共享 | PersistenceV2 globalConnect | 路径隔离更灵活 |
| 存储大数据/集合类型 | PersistenceV2(API 23+) | 超 2KB 或数组数据必须用 V2 |
5.2 V1→V2 迁移步骤
- 将组件装饰器从
@Component替换为@ComponentV2。 - 将
@StorageLink/@StorageProp替换为 V2 版本(注意 API 签名差异)。 - 替换
PersistentStorage.persistProp()为PersistenceV2.connect()。 - 注意:V1 和 V2 的状态管理装饰器不能混用在同一个组件中,建议逐个组件迁移。
5.3 完整迁移示例
// 改造前(V1)
PersistentStorage.persistProp('userName', 'Guest');
@Entry
@Component
struct Profile {
@StorageLink('userName') name: string = 'Guest';
// ...
}
// 改造后(V2)
@Entry
@ComponentV2
struct Profile {
@Local name: string = PersistenceV2.connect('userName', 'Guest');
// ...
}
六、最佳实践要点
- 不要持久化过多数据:持久化存储适合 UI 状态,不适合业务大文件;大文件应使用文件管理 API。
- 避免频繁写入:虽然 V2 是异步写入,但高频写操作会触发多次磁盘 I/O,建议合并写操作。
- 默认值处理:
connect的第二个参数是默认值,仅在磁盘无该 key 时生效;如需重置,需主动调用PersistenceV2.remove(key)。 - 测试冷启动恢复:务必在真机或 DevEco Studio 模拟器上做冷启动测试,确认数据恢复逻辑正确。
七、总结
鸿蒙应用开发中的持久化方案从 V1 的 PersistentStorage 演进到 V2 的 PersistenceV2,核心变化在于:性能更优、数据类型更丰富、与状态管理引擎集成更紧密。对于新开发的项目,应优先选择 V2 方案;对于存量 V1 项目,可按组件粒度逐步迁移。
持久化存储只是鸿蒙状态管理的一部分,建议结合 @State、@Prop、@Provide/@Consume 等装饰器综合使用,打造流畅且数据不丢失的用户体验。
更多推荐




所有评论(0)