【听见课堂 HarmonyOS NEXT 实战系列 06】PersistenceV2 轻量持久化:主题、字号与最近页面

在 HarmonyOS NEXT 应用里,“需要重启后保留”不等于“全部塞进数据库”。主题、字幕字号、是否同意隐私说明等设置很轻;课程、字幕、板书和任务却需要查询、关联、统计与迁移。如果把两类数据放进同一套存储,代码会越来越难解释,删除范围也容易失控。

听见课堂采用一条简单边界:显示偏好和少量恢复状态交给 PersistenceV2,结构化课堂记录交给 RelationalStore。本文结合项目中的 UserPreferencesIndex.ets,拆解这条边界怎样落到真实代码。

听见课堂 PersistenceV2 轻量偏好持久化

一、先判断状态的“重量”,再选择存储

判断一个状态是否适合 PersistenceV2,可以先问四个问题:

  1. 它是不是单个用户偏好或少量键值状态?
  2. 它是否不需要复杂筛选、排序、统计和关联查询?
  3. 它是否可以用明确默认值恢复?
  4. 删除业务数据时,它是否应该独立保留?

若答案大多为“是”,它通常适合轻量持久化。反之,存在列表、来源关系、事务、索引或迁移要求的数据,更适合 RelationalStore

数据 项目选择 原因
隐私说明已同意 PersistenceV2 单个布尔值,决定启动门禁
暗色模式 PersistenceV2 显示偏好,需要跨页面和重启保留
字幕字号、高对比度 PersistenceV2 少量可恢复的无障碍设置
最近页面 PersistenceV2 轻量恢复状态,不需要查询
课程、字幕、板书、任务 RelationalStore 需要关联、筛选、统计、事务与迁移
当前筛选、打开的面板、编辑草稿 页面短生命周期状态 不应因一次临时操作污染下次启动

PersistenceV2 与 RelationalStore 的数据边界

二、UserPreferences 只保存轻量偏好

项目在 common-core/src/main/ets/index.ets 中集中定义偏好模型:

@ObservedV2
export class UserPreferences {
  @Trace privacyAccepted: boolean = false;
  @Trace darkMode: boolean = false;
  @Trace lastPage: string = RouteId.PRIVACY;
  @Trace keepRawAudio: boolean = false;
  @Trace captionFontScale: number = 1;
  @Trace highContrastCaptions: boolean = false;
  @Trace aiSuggestionsEnabled: boolean = true;
}

这里有两个值得保留的设计点。

第一,字段都有清晰默认值。首次安装、持久化对象不存在或恢复失败时,应用仍能得到可解释的初始状态。第二,模型没有放入课程数组、字幕列表或任务对象;它没有演变成另一个“迷你数据库”。

keepRawAudio 虽然也是轻量开关,但开关本身和录音文件不是一回事。即使用户选择保存原始音频,文件的创建、保留、删除和导出仍应由受控的媒体与数据流程负责,不能因为一个布尔值为 true 就让页面随意写文件。

三、用 connect 建立唯一偏好实例

entry/src/main/ets/pages/Index.ets 中,项目通过稳定 key 连接持久化对象:

const PERSISTED_PREFERENCES: UserPreferences = PersistenceV2.connect(
  UserPreferences,
  'heard-classroom-preferences',
  () => new UserPreferences()
) ?? new UserPreferences();

组件再持有这个对象:

@Local private preferences: UserPreferences = PERSISTED_PREFERENCES;

@Local private currentPage: string = this.preferences.privacyAccepted
  ? this.preferences.lastPage
  : RouteId.PRIVACY;

这使启动恢复规则非常清楚:未同意隐私说明时始终进入 P01;已经同意时,才尝试恢复最近页面。持久化不是为了绕过隐私门禁,而是服务于门禁之后的体验恢复。

实际项目还应校验 lastPage 是否仍在当前路由集合中。版本升级后若某个页面已被删除,未知 route key 应安全回退到首页,不能渲染空白页。

四、修改偏好后显式保存,并让当前页面立即响应

听见课堂把设置保存收敛到一个小方法:

private saveSettingsPreference(message: string): void {
  PersistenceV2.save(UserPreferences);
  this.notice = message;
}

private setCaptionFontScale(scale: number): void {
  this.preferences.captionFontScale = scale;
  this.saveSettingsPreference(
    '字幕字号已调整为' + this.captionFontScaleLabel() +
    ',实时课堂同步生效'
  );
}

用户点击“大号”后,同一 preferences 对象立即影响设置预览和 P04 实时字幕卡;save 又保证强制结束应用后仍能恢复。高对比、暗色模式和本机智能整理开关采用相同路径。

这同时满足两个不同目标:

  • 当前会话响应:状态对象变化后,关联页面立刻重新渲染;
  • 进程重启恢复:显式保存后,下次连接到同一 key 能读取旧值。

不能只看到“当前页面变了”就认定持久化成功。真正的验收至少要包含:修改设置、跳转到受影响页面、强制结束进程、重新打开应用、再次回读设置和受影响页面。

五、最近页面也应该通过统一导航入口保存

项目的 navigate 不只是改 currentPage,还负责保存最近页面:

private navigate(route: RouteId): void {
  if (route !== this.currentPage) {
    this.contentScroller.scrollEdge(Edge.Top);
  }
  this.currentPage = route;
  this.preferences.lastPage = route;
  PersistenceV2.save(UserPreferences);
}

如果每个按钮分别写 currentPage = ...,就会出现有的入口能恢复、有的入口不能恢复。集中导航入口让滚动复位、实时课堂暂停和最近页面保存拥有一致行为。

但并非所有导航状态都应持久化。例如当前展开的设置分组、搜索筛选、候选任务编辑草稿,更适合停留在页面短生命周期状态。否则用户第二天启动应用,可能直接落在一个过时的弹层或半完成表单里。

六、清除课堂数据为什么不删除显示偏好

听见课堂的“删除全部课堂数据”会清理课程、字幕、板书和任务,但成功提示明确写着:

this.notice = '全部课堂数据已从本机删除;设置偏好仍保留';

这是数据语义决定的,而不是技术上的遗漏。

  • 课程与字幕属于用户创建的课堂域数据;
  • 暗色、字幕字号和高对比度属于使用应用的显示偏好;
  • 删除课堂历史,不代表用户希望无障碍设置恢复默认;
  • 系统权限和应用外文件更不能被一个课堂数据按钮顺带修改。

项目的破坏性流程验收实际验证了:课堂表清空为 0/0/0/0 后,字幕字号仍保持“大号”;强制结束再启动后,课堂数据没有回填,偏好也仍被保留。随后测试环境才单独恢复演示数据与标准字号。

如果产品需要“恢复出厂设置”,它应该成为另一个影响范围更大的动作,并单独列出:偏好、课堂数据、缓存、文件、权限能否或不能被重置。不要把它偷偷合并到“删除课堂记录”。

七、常见误区与改进建议

误区 1:只要想跨页面共享,就全部持久化

跨页面共享与跨进程持久化是两件事。当前会话的临时筛选、弹窗和输入草稿,使用组件状态、ViewModel 或轻量刷新信号即可,不必写入磁盘。

误区 2:把 PersistenceV2 当成列表数据库

字幕、扫描记录和任务需要来源 ID、时间、状态、筛选与迁移。它们应由 Repository 访问 RelationalStore,页面写入后重新读取 canonical data,而不是把数组塞进偏好对象。

误区 3:当前切换成功就等于重启恢复成功

UI 立即变化只证明响应式状态生效。必须补进程强停、重新启动和旧版本升级测试,才能分别证明重启恢复与迁移。

误区 4:删除动作按“存储技术”划范围

用户不关心某字段存在 PersistenceV2 还是数据库里。删除确认应按业务语义列出“课程、字幕、板书、任务、偏好、文件”,并清楚说明保留项。

八、一份可复用的验收清单

场景 预期
首次安装 使用安全默认值,先经过隐私门禁
修改字幕字号 设置预览与实时字幕立即变化
切换暗色模式 背景、正文、边界和禁用态一起适配
关闭本机智能整理 候选停止展示,已确认任务不删除
强制结束再启动 已保存偏好恢复
删除全部课堂数据 课堂记录清空,显示偏好保留
未知最近页面 回退到安全入口
应用升级 旧偏好可迁移或使用明确默认值

听见课堂当前已经验证了设置即时联动、强停恢复和删除课堂数据后偏好保留;设备整机重启、旧版本升级迁移与系统 1.5 倍字体仍应单独补验,不能用同进程刷新代替。

总结

PersistenceV2 的价值不在于替代数据库,而在于用很小的模型保存真正轻量、稳定、可默认恢复的用户偏好。听见课堂把隐私同意、暗色、字幕字号、高对比、本机 AI 开关和最近页面放在 UserPreferences,把课程、字幕、板书和任务留给 RelationalStore

清晰的存储边界,也让删除范围更符合用户直觉:清除课堂数据不应顺手抹掉无障碍显示偏好。下一篇将进一步讨论能力真实性,解释为什么“代码能编译、页面能运行”仍不能直接写成“AI 能力已经完成”。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐