第 19 天|Preferences:轻量缓存与状态恢复

12 阅读3分钟
本篇看点
  • 阅读目标:理解 Preferences 的轻量键值边界,并和 RDB 的业务列表场景区分开。
  • 核心问题:Preferences 适合轻量键值,不适合承载复杂业务列表。
  • 对比迁移:对应 iOS UserDefaults 和 Flutter shared_preferences。
前言
本地缓存可以先区分数据形态。主题、语言、开关、点赞状态、首次启动标记,这些轻量键值数据,用 Preferences 更合适。
iOS 里对应 UserDefaults,Flutter 里常用 shared_preferences。HarmonyOS 的 Preferences 也是这类能力:轻量、键值、适合小数据。
一. Preferences 适合什么
适合:
  1. 暗黑模式选择。
  2. 多语言选择。
  3. 用户设置开关。
  4. 文章点赞 id 集合。
  5. 搜索历史少量数据。
  6. 首次启动引导标记。
不适合:
  1. 大列表。
  2. 下载记录。
  3. 播放历史。
  4. 复杂查询。
  5. 多表关系。
后面这些应该交给 RDB。
二. 用服务层封装 Preferences
页面直接调用系统 API 会让代码很难测,也很难替换缓存方案。建议先抽一个接口:
export interface KeyValueStorage {
  getString(key: string, defaultValue: string): Promise<string>;
  putString(key: string, value: string): Promise<void>;
  getBoolean(key: string, defaultValue: boolean): Promise<boolean>;
  putBoolean(key: string, value: boolean): Promise<void>;
}
页面或 Store 只依赖接口,不关心底层是 Preferences、内存 mock,还是后续迁移到安全存储。
三. 点赞缓存怎么设计
资讯列表里的点赞状态适合 Preferences,因为它是小规模键值状态。
class LikeStore {
  private likedIds: Set<string> = new Set<string>();

  async load() {
    const raw = await this.storage.getString('liked_article_ids', '[]');
    const ids: string[] = JSON.parse(raw) as string[];
    this.likedIds = new Set<string>(ids);
  }

  async toggle(id: string) {
    if (this.likedIds.has(id)) {
      this.likedIds.delete(id);
    } else {
      this.likedIds.add(id);
    }
    await this.storage.putString('liked_article_ids', JSON.stringify(Array.from(this.likedIds)));
  }
}
这里有两层状态:内存里的 Set 用于快速判断,Preferences 用于跨启动恢复。
四. UI 局部刷新怎么配合
点赞按钮的刷新范围可以尽量小。把点赞状态收敛到单个 item 或按钮组件,列表会更稳:
LikeBurstButton({
  liked: item.liked,
  onToggle: () => {
    this.onToggleLike(item.id);
  }
})
Store 更新某个 item 后,只让对应行重新渲染。这个案例很适合和 @Observed/@ObjectLink 或不可变 item 更新一起讲。
五. Preferences 也要处理异常
本地缓存读写看起来稳定,但仍然可能失败,比如 JSON 格式损坏、版本升级字段变化。
所以读取时要有兜底:
function parseStringArray(raw: string): string[] {
  try {
    const value = JSON.parse(raw) as string[];
    return Array.isArray(value) ? value : [];
  } catch (_) {
    return [];
  }
}
真实项目里还可以加版本号,方便以后迁移。
六. 三端对比
场景HarmonyOSFlutteriOS
轻量键值Preferencesshared_preferencesUserDefaults
大量结构化数据RDBsqflite / driftSQLite / CoreData
安全敏感数据安全存储策略flutter_secure_storageKeychain
所以选型思路很简单:小设置用键值,业务列表用数据库,敏感数据用安全存储。
七. 实践经验
第一,把完整列表 JSON 全塞进 Preferences,后期查询和迁移都痛苦。
第二,页面到处直接读写 key,key 名无法统一管理。
第三,只改内存状态,忘了持久化,重启后丢失。
第四,读取缓存没有异常兜底,一次坏数据导致页面打不开。
小结
Preferences 是鸿蒙本地缓存的第一站。它最适合轻量设置和少量状态,配合 Store 可以做出很自然的跨启动恢复。
下一篇我们讲 RDB,把播放历史、订阅、下载记录这些真正的业务数据放进结构化存储里。
今日练习
  1. 用 Preferences 缓存一个暗黑模式开关,重启后恢复。
  2. 用字符串数组保存点赞 id 集合,并处理 JSON 解析失败兜底。
  3. 说明为什么收听历史不适合直接塞进 Preferences。