HarmonyOS 本地持久化实战:Preferences、schema version 与空状态初始化

HarmonyOS 本地持久化实战:Preferences、schema version 与空状态初始化

新建卡片验证截图

这个项目是一个桌面卡片工具,用户的卡片、收藏、回收站、提醒、主题、样式、备份信息和桌面 Form 选择都需要保存到本地。项目没有引入复杂数据库,而是使用 HarmonyOS preferences 做轻量持久化。

这篇讲 AppDataService.ets 里的持久化设计,重点是 schema version、空状态初始化、快照备份和页面刷新。

状态边界:所有业务状态收进一个 snapshot

共享模型中定义了完整状态:

export interface AppStateSnapshotModel {
  profile: UserProfileModel;
  cards: CardRecordModel[];
  recycleBin: CardRecordModel[];
  desktopCardId: string;
  desktopFormIds: string[];
  reminder: ReminderSettingsModel;
  selectedThemeId: string;
  selectedStyleId: string;
  backupMeta: BackupMetaModel;
  activityLog: ActivityRecordModel[];
}

这不是唯一选择,但对一个轻量工具应用很合适。原因是:

  • 状态量不大。
  • 大部分写操作都需要刷新多个摘要。
  • 备份和恢复可以直接处理整份 JSON。
  • 桌面 Form 只需要读取其中一部分。

Preferences key 设计

AppDataService.ets 中集中定义 key:

const PREFS_NAME = 'project028_card_tool';
const STATE_KEY = 'app_state_v1';
const STATE_SCHEMA_VERSION_KEY = 'app_state_schema_version';
const STATE_SCHEMA_VERSION = '2';
const LAST_BACKUP_KEY = 'last_backup_snapshot_v1';
const SYSTEM_BACKUP_FILE = 'project028-backup.json';

这里有一个细节:STATE_KEY 仍然叫 app_state_v1,但真正控制迁移的是 STATE_SCHEMA_VERSION。这样做可以避免 key 名频繁变化,同时保留清晰的版本边界。

初始化时先判断版本

项目的发布态策略是“无版本即空状态”。如果本地没有 schema version,或者版本不匹配,就不继续沿用旧 demo 数据,而是清空为正式发布态的空状态。

伪代码可以理解为:

this.preferences = preferences.getPreferencesSync(context, { name: PREFS_NAME });
const storedVersion = this.preferences.getSync(STATE_SCHEMA_VERSION_KEY, '') as string;

if (storedVersion !== STATE_SCHEMA_VERSION) {
  this.state = this.createDefaultState();
  this.persistState();
  return;
}

const rawState = this.preferences.getSync(STATE_KEY, '') as string;
this.state = this.normalizeState(JSON.parse(rawState));

这个策略适合上架前从 demo 数据切到真实用户状态。否则用户第一次打开应用,可能会看到开发阶段预置的样例卡片。

createDefaultState 不等于塞满示例数据

项目早期为了展示页面效果,会构造一些默认卡片。后面发布态改成:真实用户数据默认空,首页和分类页通过内置模板目录撑起首屏。

这意味着:

  • cards 可以为空。
  • recycleBin 可以为空。
  • backupMeta 是空备份信息。
  • 首页推荐和分类概览不依赖真实用户数据。

这样既能保证首屏完整,又不会把 demo 数据当成用户数据持久化。

normalizeState:让旧数据不直接污染页面

持久化数据来自本地文件,必须防御字段缺失、类型变化和旧版本状态。项目用 normalizeState()normalizeCard()normalizeReminder() 等方法兜底。

例如草稿和卡片保存时,页面只提交当前字段,服务层负责补齐:

private normalizeDraft(draft: CardDraftModel): CardDraftModel {
  return {
    id: draft.id,
    templateId: draft.templateId,
    title: draft.title.trim(),
    subtitle: draft.subtitle.trim(),
    detail: draft.detail.trim(),
    value: draft.value.trim(),
    footer: draft.footer.trim(),
    badge: draft.badge.trim(),
    tone: this.normalizeTone(draft.tone, 'brand'),
    categoryId: this.normalizeCategory(draft.categoryId, 'tool'),
    favorite: draft.favorite
  };
}

这类 normalize 不只是为了“代码好看”,而是为了避免页面直接拿到非法状态。

每次写操作都记录 activity

项目有统计页,所以创建、编辑、归档、恢复、备份、主题切换都会写活动日志:

private recordActivity(type: ActivityType, title: string, weight: number, date: Date): void {
  const record: ActivityRecordModel = {
    id: this.generateId('activity'),
    type: type,
    title: title,
    createdAt: formatDateTime(date),
    dayKey: createDateKey(date),
    weight: Math.max(1, weight)
  };
  this.state.activityLog.unshift(record);
  if (this.state.activityLog.length > 60) {
    this.state.activityLog = this.state.activityLog.slice(0, 60);
  }
}

这里限制最多 60 条,是为了让轻量 Preferences 不无限增长。统计页需要的是趋势,不是完整审计日志。

持久化写入:putSync + flushSync

服务层的写入方式是同步写入:

this.preferences.putSync(STATE_SCHEMA_VERSION_KEY, STATE_SCHEMA_VERSION);
this.preferences.putSync(STATE_KEY, JSON.stringify(this.state));
this.preferences.flushSync();

对这个项目来说,同步写更直接:写操作来自按钮点击,数据量小,页面需要立刻刷新摘要。如果是大量数据或高频写入,就需要改成异步和节流。

备份快照复用同一份状态

本地“立即备份”不是另起一套数据结构,而是导出当前 snapshot:

private exportSnapshot(): string {
  return JSON.stringify(this.state);
}

恢复时再走 importSnapshot()normalizeState()。这样备份恢复和版本兼容可以共享一套逻辑。

页面刷新时机

各页面常见写法是:

aboutToAppear(): void {
  this.refreshData();
}

onPageShow(): void {
  this.refreshData();
}

这是因为卡片可能从详情页、编辑页、管理页、备份页等不同入口被修改。页面重新显示时从服务层取最新视图模型,比跨页传一堆状态更稳。

常见坑

  1. 不要把 demo 数据直接当默认用户状态。
  2. 不要只在 STATE_KEY 上做版本判断,最好单独存 schema version。
  3. 不要让页面直接修改全局 state,写操作集中到服务层。
  4. 恢复备份后要重新计算摘要、统计和桌面卡片数据。
  5. 旧数据字段缺失时必须 normalize,不能直接断言类型正确。

小结

这个项目的本地持久化思路可以概括为一句话:Preferences 保存整份轻量状态,schema version 控制迁移边界,服务层统一 normalize 和写入。

对卡片工具这类应用来说,这种方案足够简单,也足够稳定。真正要注意的不是 API 调用,而是“默认状态、旧数据、页面刷新、备份恢复”这四件事是否在同一套规则里。

源码链接: https://pan.quark.cn/s/dbe32f6bace6 在本指南中,我们将详细解析如何在银河麒麟v10操作系统平台上完成MySQL 5.7的安装过程。银河麒麟v10作为一个基于Linux内核的国产操作系统,特别适用于arm架构的aarch64计算平台。鉴于我们讨论的是免编译的安装方法,这意味着我们将借助预先编译好的二进制软件包来简化操作步骤,而非采用从源代码开始的编译方式。 ### 一、前期准备 1. **系统更新**: 在部署任何新软件之前,务必确保操作系统处于最新状态,此举旨在规避潜在的兼容性挑战和已知的安全隐患。 ``` sudo apt-get update sudo apt-get upgrade ``` 2. **依赖安装**: MySQL 5.7版本在运行时可能需要特定的库文件支持,比如libaio和jemalloc。在银河麒麟v10环境中,可以通过以下指令来安装这些必需的依赖项: ``` sudo apt-get install libaio1 libaio-dev jemalloc-dev ``` ### 二、获取MySQL 5.7二进制文件 由于银河麒麟v10运行在arm架构之上,因此需要寻找适配aarch64架构的MySQL 5.7二进制文件。这些文件可从MySQL的官方发布渠道或授权的第三方镜像站点获取。务必确认下载的文件名压缩包内的内容一致。例如,文件名应为`mysql-5.7.37-linux-glibc2.17-arm64.tar.gz`。 ### 三、部署MySQL 5.7 1. **文件解压缩**: 将下载的MySQL压缩文件解压至一个指定目录,例如 `/usr/local/`。 ``` tar...
下载代码方式:https://pan.quark.cn/s/a4b39357ea24 Node.js 是一种开放源代码且能够在多种操作系统上运行的 JavaScript 执行环境,它使得开发人员能够在服务器端执行 JavaScript 代码。Node.js 采用了 V8 引擎,该引擎是由 Google 为 Chrome 浏览器开发的一个高性能的 JavaScript 解释器。Node.js 的 16.x 版本在其发展历程中占据着重要位置,其中包含了众多新功能以及性能上的改进。标题 "Nodejs16-x64 windows安装包" 指向的是专为 Windows 操作系统设计的 64 位版本的 Node.js 16 安装程序。在 Windows 平台上安装 Node.js 的 64 位版本对于处理大量数据或运行需要高性能的应用程序来说尤为关键,因为 64 位系统能够更有效地利用硬件资源。描述 "Nodejs-16 x64位windows 安装包" 明确了该安装程序是为 Windows 用户准备的,特别是对于那些需要运行 64 位应用程序的用户。x64 表明该版本兼容 64 位架构,意味着它能够充分利用 64 位计算机的内存和处理能力。标签 "Node Nodejs nodejs16" 提供了关于此安装包的核心信息,表明它 Node.js 相关,并且具体指的是 v16 版本。这些标签有助于进行搜索和分类,从而方便用户找到他们所需要的特定版本。压缩包文件 "node-v16.18.0-x64.msi" 代表实际的安装文件,其中 "v16.18.0" 指示了 Node.js 的具体版本号,"x64" 再次强调了其适用于 64 位系统,而 ".msi" 后缀表明这是一...
打开链接下载源码: https://pan.quark.cn/s/a4b39357ea24 打印机驱动程序充当了计算机打印机之间的关键接口,它确保了不同硬件设备在各种操作系统环境下都能精确地执行打印任务。在现代办公环境中,一个性能稳定的打印机驱动对于达成高效率和高质量的打印输出具有决定性作用。震旦打印机18BW-7作为一款专为商务办公设计的黑白激光打印机,其驱动程序的设计和兼容性显得尤为重要。本篇将全面分析震旦打印机18BW-7驱动的特性、安装流程,以及用户在使用时应当注意的事项。 震旦打印机18BW-7的打印机驱动被设计为能够适配32位和64位两种架构的计算机系统。该驱动程序的多平台适应性确保了无论用户采用何种计算机配置,震旦打印机18BW-7都能获得充分的系统支持和优异的表现。针对该打印机的驱动程序不仅包含了核心的设备驱动程序,而且还集成了用户交互界面和可能的辅助软件组件。 设备驱动程序部分,它负责处理操作系统打印机之间的基础通信。它能够接收来自计算机的操作指令,然后将其转换为打印机能够识别的信号,以此来控制打印机完成各种打印任务。同时,当打印机需要向操作系统反馈状态信息时,设备驱动程序同样扮演着信号转换的角色。 用户界面是驱动程序不可或缺的一部分,它为用户提供了直观的图形操作环境。借助用户界面,用户可以便捷地进行打印机的安装、设置、监控打印进度以及进行故障诊断等操作。这显著降低了普通用户进行日常维护的难度,使得打印机的操作更加便捷。 对于支持网络打印功能的打印机,辅助软件是必不可少的组成部分。网络打印辅助软件负责处理网络层面的数据传输,确保打印数据能够安全且精确地传输至打印机,从而实现远程打印或网络打印任务的管理等高级功能。 震旦打印机18BW-7的驱动安...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值