端上为什么需要分层存储

应用的数据不是一个模子刻出来的:开关状态、字号选择这类只有几个字节的偏好,和成千上万条结构化记录,显然不该放在一起。鸿蒙把存储能力分成了几个层次,选择哪一层,取决于数据的体量、结构与是否需要跨端同步。

一个实用的判断标准:数据量小、结构简单、只需本机读取的,用 Preferences;数据是结构化的、需要条件查询与排序的,用关系型数据库;需要在手机、平板、车机等多设备之间自动保持一致的,用分布式数据对象。把这三层的边界想清楚,后面的代码几乎是自然推导出来的。

不要一上来就用数据库存一切。多数应用一半以上的数据其实只是几个配置项,用对工具比用强工具更重要。

这篇文章按从轻到重的顺序,把三层存储都走一遍,并在最后串成一个可跨端同步的笔记列表。

Preferences:轻量键值与配置

Preferences 是基于键值的轻量存储,适合保存用户偏好与小型状态。它的特点是读写快、以全量文件形式驻留,因此不要往里塞大对象或高频写入的列表。

import { preferences } from '@kit.ArkData';

const store = preferences.getPreferencesSync(getContext(), { name: 'user_pref' });

// 写入:键值对,第二个参数是默认值
store.putSync('font_size', 16);
store.putSync('dark_mode', false);
store.flush();   // 异步落盘,频繁写入时批量后再 flush

// 读取
const fontSize = store.getSync('font_size', 16) as number;

两个细节要注意:一是 put 之后数据先在内存,真正写入磁盘要等 flush,退出应用前记得触发一次;二是键名建议集中管理成常量,避免不同页面各写各的导致拼写不一致。监听变化用订阅接口,可以在深色模式切换时即时刷新界面。

关系型数据库:结构化数据落地

当数据变成一条条带字段的记录,并且需要按条件查询、排序、分页时,就该上关系型数据库了。鸿蒙提供的关系型数据库(RelationalStore)基于 SQLite,支持 SQL 与预编译语句。

建表与索引要点
  • 字段类型文本用 TEXT,整数用 INTEGER,时间戳建议存成 Long,避免时区混乱。
  • 主键每张表都要有稳定主键,列表的 ForEach 键、同步去重都靠它。
  • 索引高频查询与排序的字段建索引,否则数据量一大列表就卡。
  • 预编译用 statement 复用 SQL,既能防注入也能提升重复查询性能。
鸿蒙数据持久化示意:中央抽象手机左右连接圆柱形数据库、方形存储立方体与发光同步节点,节点间以曲线箭头相连
分层存储:轻量配置进 Preferences,结构化记录进关系库,跨端一致交给分布式数据对象。

查询时务必带分页条件,不要一次把整张表拉进内存。配合 @State 驱动列表渲染时,建议把数据库操作放在异步任务里,查询完成再整体替换数组,避免在主线程做磁盘 IO 造成卡顿。

分布式数据对象:多端自动同步

分布式数据对象(DistributedDataObject)是鸿蒙很有特色的能力:你把它当成一个普通对象读写,框架负责在同一账号下的多台设备之间自动同步,省去了自己写同步协议。

import { distributedDataObject } from '@kit.ArkData';

// 创建一个可同步对象,并订阅同账号下的其他设备
const obj = distributedDataObject.create(getContext());
obj.set('note', '第一条笔记');
obj.set('done', false);

obj.on('status', (key: string, status: string) => {
  // status 变化时刷新界面:远端改了,本地自动收到
  console.info('远端更新:%{public}s => %{public}s', key, status);
});

它适合配置、购物车、编辑草稿这类小体量、强一致的场景。需要清醒认识的两点:同步依赖同一华为账号与分布式网络环境,离线时先写本地、联网再合并;冲突策略通常是后写覆盖,重要业务要自己加版本字段做更精细的合并。

数据迁移与版本升级

应用版本迭代几乎必然涉及表结构变化。关系型数据库支持配置版本号与升级回调:当本地数据库版本低于新版本时,框架会触发 onUpgrade,你在这里写 ALTER TABLE 等迁移语句。

  • 新增列用 ALTER TABLE ADD COLUMN,并给默认值,避免老数据读出空值。
  • 删列或改类型通常要新建表、拷贝数据、再替换,迁移前后都要备份。
  • 迁移逻辑必须幂等、可重复执行,否则一次失败就会把用户数据搞坏。
  • 上线前用旧版本真实数据反复演练升级流程,比在模拟器里空跑可靠得多。
数据迁移是最不能"先上线再说"的环节。一旦迁移脚本有 bug,丢掉的是真实用户的本地数据,没有第二次机会。

加密与敏感数据保护

手机号、定位、账号凭据这类数据不能裸奔在文件里。关系型数据库支持加密能力,Preferences 也可配合应用级安全存储,把敏感字段加密后再落盘。

实践上有三条底线:第一,密钥不硬编码在代码里,交给系统的密钥管理能力;第二,日志里不要打印敏感字段,hilog 也要注意脱敏;第三,卸载重装或用户登出时,按合规要求清理对应数据。这条线和下一篇要讲的应用安全与权限管理正好衔接起来。