鸿蒙实战:AppStorage 全局状态管理
鸿蒙实战:AppStorage 全局状态管理
前言(必读)

图:鸿蒙实战:AppStorage 全局状态管理(第 72 篇) 运行效果截图(HarmonyOS NEXT)
在跨页面共享数据时,如果每个页面都从数据库重新加载一遍,不仅代码冗余,性能也会受影响。HarmonyOS 提供了 AppStorage——应用级别的全局键值存储引擎,数据在内存中常驻,所有页面都能直接读写。本文从项目中的 7 个 AppStorage 键入手,详解全局状态管理的设计。

图:AppStorage 全局状态管理架构——7 个核心键的写入来源与读取页面
一、AppStorage 核心 API
// 写数据
AppStorage.setOrCreate<number>('key', value)
// 读数据
const value = AppStorage.get<number>('key') ?? defaultValue
// 删除
AppStorage.delete('key')
| API | 说明 | 区别 |
|---|---|---|
setOrCreate(key, value) |
设置或创建键值 | 取代旧的 set,自动判断是否存在 |
get<T>(key) |
读取数据 | 返回 T | undefined |
delete(key) |
删除键值 | 清理不需要的数据 |
keys() |
获取所有键 | 调试用 |
重要特点:AppStorage 存储在内存中,应用退出后数据自动清除。它适合全局状态共享,不适合持久化(持久化用 RDB 数据库)。
二、项目中全部 7 个 AppStorage 键
2.1 数据全景
// AppStorage 键值全景(项目中完整使用)
// ── 用户身份 ──
AppStorage.setOrCreate<number>('user_id', 1) // 用户 ID
AppStorage.setOrCreate<string>('user_name', '鹿鹿') // 用户昵称
// ── 导航与页面跳转 ──
AppStorage.setOrCreate<number>('current_report_id', 0) // 当前查看的报告 ID
AppStorage.setOrCreate<number>('current_archive_id', 0) // 当前查看的档案 ID
// ── 拍照流程 ──
AppStorage.setOrCreate<string>('capture_image_path', '')// 拍照临时文件路径
// ── 分析结果 ──
AppStorage.setOrCreate<Record<string, Object>>('analysis_result', {}) // 分析结果对象
// ── 伴侣关系 ──
AppStorage.setOrCreate<string>('couple_partner_name', '') // 伴侣昵称
AppStorage.setOrCreate<string>('couple_bound', '1') // 是否已绑定
2.2 各键用途速查
| 键名 | 类型 | 写入位置 | 读取位置 | 用途 |
|---|---|---|---|---|
user_id |
number | LoginPage | 所有需要 userId 的页面 | 数据库查询参数 |
user_name |
string | LoginPage | HomePage/MePage/TapSharePage/SharePage | 用户显示名 |
current_report_id |
number | HomePage/ArchivePage | ReportDetailPage | 跳转报告详情 |
current_archive_id |
number | ArchivePage | ReportDetailPage | 跳转档案详情 |
capture_image_path |
string | CapturePage | AnalyzingPage | 传递拍照路径 |
analysis_result |
object | AnalyzingPage | ReportDetailPage/SharePage/TapSharePage | 分析结果共享 |
couple_partner_name |
string | CoupleHomePage | MePage/DuetReportPage | 伴侣显示名 |
couple_bound |
string | CoupleHomePage | CoupleHomePage | 是否绑定标记 |
三、典型数据流
3.1 登录 → 首页
LoginPage AppStorage HomePage
│ │ │
│ login() │ │
│ ├─ setOrCreate('user_id',…) │ │
│ ├─ setOrCreate('user_name',…)│ │
│ │ │
│ router.replaceUrl() │ aboutToAppear() │
│ ─────────────────────────► │ ◄────────────────────────┐ │
│ │ AppStorage.get('user_name') │
│ │ │
// LoginPage.ets — 写入
AppStorage.setOrCreate<number>('user_id', user.id)
AppStorage.setOrCreate<string>('user_name', user.name)
this.getUIContext().getRouter().replaceUrl({ url: 'pages/HomePage' })
// HomePage.ets — 读取
aboutToAppear(): void {
const name = AppStorage.get<string>('user_name')
if (name) this.userName = name
}
3.2 拍照 → 分析 → 报告
CapturePage → AnalyzingPage → ReportDetailPage
│ │ │
│ save temp image │ │
│ setOrCreate( │ │
│ 'capture_image_path')│ │
│ │ │
│ │ 分析完成后 │
│ │ setOrCreate( │
│ │ 'analysis_result', …) │
│ │ │
│ │ │ AppStorage.get(
│ │ │ 'analysis_result')
│ │ │ AppStorage.get(
│ │ │ 'current_report_id')
3.3 档案列表 → 报告详情
// 方案:AppStorage + router.params 双重传递
// ArchivePage.ets
.onClick(() => {
AppStorage.setOrCreate<number>('current_archive_id', item.id)
this.getUIContext().getRouter().pushUrl({
url: 'pages/ReportDetailPage',
params: { archiveId: item.id }
})
})
// ReportDetailPage.ets — 接收端兜底逻辑
aboutToAppear(): void {
// 优先从 router.params 读取
const params = this.getUIContext().getRouter().getParams()
// 再从 AppStorage 兜底
const archivedId = AppStorage.get<number>('current_archive_id') ?? 0
}
四、与其他存储方案的对比
// 本项目三层存储体系
┌─────────────────────────────────────────────────┐
│ 第一层:AppStorage(内存) │
│ 特点:极快、非持久化、跨页面 │
│ 用途:userId / 当前跳转 ID / 临时数据 │
├─────────────────────────────────────────────────┤
│ 第二层:@State + @Prop(组件内) │
│ 特点:自动触发 UI 重渲染 │
│ 用途:页面局部状态 │
├─────────────────────────────────────────────────┤
│ 第三层:RDB 数据库(SQLite) │
│ 特点:持久化、复杂查询、CRUD │
│ 用途:用户数据 / 笔迹记录 / 报告 │
└─────────────────────────────────────────────────┘
| 维度 | AppStorage | 数据库 (RDB) | @State |
|---|---|---|---|
| 存储位置 | 内存 | 磁盘文件 | 组件实例 |
| 生命周期 | 应用运行期间 | 永久 | 组件生命周期 |
| 读写性能 | ns 级 | ms 级 | 自动刷新 |
| 跨页面访问 | ✅ 所有页面 | ✅ 所有页面 | ❌ 仅当前组件 |
| 容量限制 | 无(受内存限制) | 数百 MB | 小(UI 状态) |
| 典型场景 | 跨页面传 ID | 持久化数据 | 按钮动画状态 |
五、最佳实践
5.1 什么时候用 AppStorage
✅ 应该用 AppStorage:
// 1. 跨页面传递 ID(最佳场景)
AppStorage.setOrCreate('current_report_id', reportId)
// 2. 全局用户身份
AppStorage.get<number>('user_id')
// 3. 临时流程数据(拍照→分析→报告)
AppStorage.setOrCreate('analyse_result', result)
❌ 不应该用 AppStorage:
// 1. 列表数据(应走数据库)
AppStorage.setOrCreate('archives', bigArray) // ❌
// 2. 敏感凭证(AppStorage 在内存未加密)
AppStorage.setOrCreate('token', jwtString) // ❌
// 3. UI 局部状态(应用 @State)
AppStorage.setOrCreate('btnScale', 0.95) // ❌
5.2 类型安全
// 读取时使用泛型确保类型安全
const userId = AppStorage.get<number>('user_id') ?? 0
// 写入时使用同名泛型
AppStorage.setOrCreate<number>('user_id', userId)
5.3 初始化时机
AppStorage 数据的写入时机和读取时机需确保时序正确:
// ❌ 可能出错:在 AppStorage 写入前读取
aboutToAppear() {
const fromApp = AppStorage.get<string>('user_name')
// 如果用户还没有登录过,这里返回 undefined
}
// ✅ 正确:提供默认值兜底
aboutToAppear() {
const fromApp = AppStorage.get<string>('user_name') ?? '默认名称'
}
六、AppStorage vs PersistentStorage
HarmonyOS 还提供了 PersistentStorage,它与 AppStorage 类似但支持持久化到文件:
// PersistentStorage 用法
PersistentStorage.persistProp('user_name', '')
// 之后可以用 AppStorage 访问
AppStorage.get<string>('user_name')
| 对比 | AppStorage | PersistentStorage |
|---|---|---|
| 持久化 | ❌ 内存级 | ✅ 文件级 |
| 跨应用重启 | ❌ 清空 | ✅ 保留 |
| 初始化 | 即时 | 需要在 UI 线程前初始化 |
| 本项目使用 | ✅ 主力 | ❌ 未使用 |
本项目控制在 AppStorage + RDB 两层,没有使用 PersistentStorage,因为需要持久化的数据(用户、档案、报告)已由 RDB 管理,AppStorage 只用作内存缓存层。
七、与 @Provide / @Consume 的对比
| 特性 | AppStorage | @Provide / @Consume |
|---|---|---|
| 作用域 | 全局(任意页面) | 组件树(父→子) |
| 类型安全 | 需手动泛型 | 自动推导 |
| 写入触发刷新 | 不自动触发 | 自动触发 |
| 跨页面 | ✅ | ❌ 仅组件树 |
| 适合 | 页面间导航 ID | 父子组件通信 |
八、注意事项与常见问题
8.1 开发注意事项
在正式开发前,建议按以下步骤完成环境准备与前置检查:
- 版本确认:检查 DevEco Studio 与 SDK 版本,确保满足目标 API Level 要求
- 权限声明:在
module.json5的requestPermissions字段中提前声明所有需要的系统权限 - 设备能力检查:调用前验证设备是否支持目标能力(相机、NFC、传感器等)
- 异步封装:所有耗时操作(数据库、文件 I/O、网络请求)统一使用
async/await处理 - 资源释放:在组件
aboutToDisappear()生命周期钩子中及时释放系统资源,防止内存泄漏
8.2 常见错误与解决方案
常见问题快速排查表:
| 问题类型 | 排查方向 | 参考方法 |
|---|---|---|
| 应用崩溃 | 查看 hilog 错误日志 | hilog.error(TAG, "...", e.message) |
| 状态丢失 | 检查 AppStorage 键名拼写 | 统一使用常量管理键名 |
| 动画不流畅 | 避免在 animateTo 回调中执行 I/O | 动画与数据操作分离 |
总结
- ✅ AppStorage 核心 API:
setOrCreate/get/delete三步操作 - ✅ 项目 7 个 AppStorage 键:user_id / user_name / report_id / archive_id / image_path / analysis_result / couple
- ✅ 三层存储体系:AppStorage(内存)→ @State(组件)→ RDB(持久化)
- ✅ 典型数据流:登录→首页、拍照→分析→报告、档案→报告详情
- ✅ 与 @State / RDB / PersistentStorage 对比:各司其职
- ✅ 最佳实践:传 ID 而非传对象、类型安全、默认值兜底
下一篇将讲解 @State 本地状态与 UI 刷新。
📌 收藏提示:AppStorage 传 ID 而非传对象——这是全局状态管理的核心原则。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- AppStorage 状态管理
- LocalStorage 页面级
- @StorageProp 装饰器
- @StorageLink 装饰器
- 第73篇 @State 本地状态
- 第74篇 路由参数
- 第99篇 设计模式回顾
- 状态管理最佳实践
七、AppStorage 深度实践
7.1 三种状态存储的对比选型
| 存储方式 | 作用域 | 生命周期 | 适合场景 |
|---|---|---|---|
@State |
组件内部 | 组件生命周期 | 临时 UI 状态 |
LocalStorage |
页面内/组件树 | 页面生命周期 | 页面级共享数据 |
AppStorage |
全应用 | 应用运行期间 | 跨页面全局数据 |
Preferences |
全应用 + 持久化 | 永久 | 用户设置/偏好 |
7.2 @StorageLink vs @StorageProp
// @StorageProp: 单向同步(AppStorage → 组件)
@StorageProp('themeMode') themeMode: string = 'light';
// 修改 this.themeMode 只影响本组件,不写回 AppStorage
// @StorageLink: 双向同步(AppStorage ↔ 组件)
@StorageLink('themeMode') themeMode: string = 'light';
// 修改 this.themeMode 会自动同步到 AppStorage,所有订阅者刷新
选择原则:只读取全局状态用 @StorageProp,需要写回用 @StorageLink。
7.3 AppStorage 键的命名规范
项目统一用常量类管理所有键名,避免字符串拼写错误:
class StorageKeys {
static readonly CURRENT_USER = 'currentUser';
static readonly IS_LOGGED_IN = 'isLoggedIn';
static readonly STATUS_BAR_HEIGHT = 'statusBarHeight';
static readonly ANALYSIS_RESULT = 'analysisResult';
static readonly COUPLE_CODE = 'coupleCode';
}
// 使用常量而非字面字符串
AppStorage.set(StorageKeys.CURRENT_USER, user);
7.4 性能注意事项
- 避免存储大型对象:AppStorage 适合存 ID、标志位等轻量数据,图片 PixelMap 等大对象应通过数据库或文件传递
- 避免频繁 set:高频更新(如滑动进度)不应写 AppStorage,会触发全局刷新
- 及时清理:登出时应清除用户相关的 key,避免内存泄漏
7.5 AppStorage 与 PersistentStorage 的配合
AppStorage 是内存存储,应用退出后数据清除。如需持久化,配合 PersistentStorage:
// 在 EntryAbility.onCreate 中初始化持久化
PersistentStorage.persistProp('themeMode', 'light');
PersistentStorage.persistProp('language', 'zh');
// 此后 AppStorage 的同名 key 会自动同步到磁盘
// 应用重启后仍能读取之前的设置
7.6 登出时清理 AppStorage
function logout() {
// 清除用户相关状态
AppStorage.delete(StorageKeys.CURRENT_USER);
AppStorage.set(StorageKeys.IS_LOGGED_IN, false);
AppStorage.delete(StorageKeys.ANALYSIS_RESULT);
AppStorage.delete(StorageKeys.COUPLE_CODE);
// 跳转登录页
router.pushUrl({ url: 'pages/LoginPage' });
}
小结:AppStorage 适合「应用级别的轻量共享状态」,PersistentStorage 负责持久化,@State 管理组件内部临时状态,三者分层配合是 HarmonyOS 状态管理的标准模式。
更多关于状态管理的详细内容,参见第73篇 @State 本地状态。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
更多推荐




所有评论(0)