鸿蒙实战:AppStorage 全局状态管理
鸿蒙实战:AppStorage 全局状态管理
前言(必读)
图:鸿蒙实战:AppStorage 全局状态管理(第 72 篇) 运行效果截图(HarmonyOS NEXT)
在跨页面共享数据时,如果每个页面都从数据库重新加载一遍,不仅代码冗余,性能也会受影响。HarmonyOS 提供了AppStorage——应用级别的全局键值存储引擎,数据在内存中常驻,所有页面都能直接读写。本文从项目中的 7 个 AppStorage 键入手,详解全局状态管理的设计。
图:AppStorage 全局状态管理架构——7 个核心键的写入来源与读取页面
一、AppStorage 核心 API
// 写数据AppStorage.setOrCreate<number>('key',value)// 读数据constvalue=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)// 用户 IDAppStorage.setOrCreate<string>('user_name','鹿鹿')// 用户昵称// ── 导航与页面跳转 ──AppStorage.setOrCreate<number>('current_report_id',0)// 当前查看的报告 IDAppStorage.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{constname=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 读取constparams=this.getUIContext().getRouter().getParams()// 再从 AppStorage 兜底constarchivedId=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 类型安全
// 读取时使用泛型确保类型安全constuserId=AppStorage.get<number>('user_id')??0// 写入时使用同名泛型AppStorage.setOrCreate<number>('user_id',userId)5.3 初始化时机
AppStorage 数据的写入时机和读取时机需确保时序正确:
// ❌ 可能出错:在 AppStorage 写入前读取aboutToAppear(){constfromApp=AppStorage.get<string>('user_name')// 如果用户还没有登录过,这里返回 undefined}// ✅ 正确:提供默认值兜底aboutToAppear(){constfromApp=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 键的命名规范
项目统一用常量类管理所有键名,避免字符串拼写错误:
classStorageKeys{staticreadonlyCURRENT_USER='currentUser';staticreadonlyIS_LOGGED_IN='isLoggedIn';staticreadonlySTATUS_BAR_HEIGHT='statusBarHeight';staticreadonlyANALYSIS_RESULT='analysisResult';staticreadonlyCOUPLE_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
functionlogout(){// 清除用户相关状态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 本地状态。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!