鸿蒙数据持久化实战:构建本地存储与云同步系统
一、章节概述
✅ 学习目标
- 掌握鸿蒙数据持久化的核心方案与应用场景
- 熟练使用 Preferences、关系型数据库、文件存储实现本地数据管理
- 理解并应用鸿蒙云同步机制构建全场景数据系统
- 实现本地与云端的数据双向同步与冲突处理
- 构建完整的数据持久化与同步解决方案
💡 重点内容
本地存储方案选型、数据库CRUD操作、文件存储权限管理、云同步机制、数据一致性保障
⚠️ 前置基础
已掌握鸿蒙 ArkTS 组件化开发、页面交互、状态管理等核心知识
二、鸿蒙数据持久化核心体系🔧
2.1 持久化方案分类与应用场景
鸿蒙提供了多层次的数据持久化方案,满足不同业务需求:
| 方案类型 | 核心特性 | 应用场景 |
|---|---|---|
| 📋 Preferences | 轻量级键值对存储、高性能查询 | 用户设置、应用配置、临时数据缓存 |
| 🗄️ 关系型数据库 | 结构化存储、事务支持、复杂查询 | 业务核心数据(如订单、待办、联系人) |
| 📁 文件存储 | 无结构存储、支持大文件 | 图片、音频、视频、文档等多媒体文件 |
2.2 核心概念
- 数据上下文 :
AbilityContext/ApplicationContext提供持久化操作的入口 - 事务:确保数据库操作的原子性、一致性、隔离性、持久性(ACID)
- 云同步:基于华为账号系统实现本地与云端数据的双向同步
三、本地存储实战⌨️
3.1 Preferences:用户设置存储
3.1.1 功能需求
构建用户设置页面,保存主题偏好(深色/浅色)和语言设置(中文/英文)
3.1.2 实现步骤
ets
// 定义常量与类型
const PREFERENCES_NAME = 'user_settings';
const KEY_THEME = 'theme';
const KEY_LANGUAGE = 'language';
// 用户设置页面
@Entry
@Component
struct SettingsPage {
// 主题状态:'light'/'dark'
@State theme: string = 'light';
// 语言状态:'zh-CN'/'en-US'
@State language: string = 'zh-CN';
// Preferences实例
private preferences: Preferences | null = null;
// 页面初始化时加载配置
async onPageShow() {
// 获取应用上下文
const context = getApplicationContext();
try {
// 初始化Preferences
this.preferences = await context.createPreferences(PREFERENCES_NAME, PreferencesConstant.Mode.MULTI_PROCESS);
// 读取存储的配置
this.theme = (await this.preferences.get(KEY_THEME, 'light')) as string;
this.language = (await this.preferences.get(KEY_LANGUAGE, 'zh-CN')) as string;
} catch (error) {
console.error('初始化Preferences失败:', error);
}
}
// 保存配置到Preferences
async saveSettings() {
if (this.preferences) {
try {
// 写入配置
await this.preferences.put(KEY_THEME, this.theme);
await this.preferences.put(KEY_LANGUAGE, this.language);
// 提交修改
await this.preferences.flushSync();
prompt.showToast({ message: '保存成功' });
} catch (error) {
console.error('保存配置失败:', error);
prompt.showToast({ message: '保存失败' });
}
}
}
build() {
Column({ space: 24 }) {
Text('用户设置')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 32 });
// 主题设置
Column({ space: 12 }) {
Text('主题模式')
.fontSize(18)
.fontWeight(FontWeight.Medium);
Row({ space: 40 }) {
Button('浅色模式')
.width(120)
.height(40)
.backgroundColor(this.theme === 'light' ? '#007DFF' : '#E5E7EB')
.fontColor(this.theme === 'light' ? Color.White : Color.Black)
.onClick(() => {
this.theme = 'light';
});
Button('深色模式')
.width(120)
.height(40)
.backgroundColor(this.theme === 'dark' ? '#007DFF' : '#E5E7EB')
.fontColor(this.theme === 'dark' ? Color.White : Color.Black)
.onClick(() => {
this.theme = 'dark';
});
}
}
// 语言设置
Column({ space: 12 }) {
Text('语言设置')
.fontSize(18)
.fontWeight(FontWeight.Medium);
Row({ space: 40 }) {
Button('中文')
.width(120)
.height(40)
.backgroundColor(this.language === 'zh-CN' ? '#007DFF' : '#E5E7EB')
.fontColor(this.language === 'zh-CN' ? Color.White : Color.Black)
.onClick(() => {
this.language = 'zh-CN';
});
Button('英文')
.width(120)
.height(40)
.backgroundColor(this.language === 'en-US' ? '#007DFF' : '#E5E7EB')
.fontColor(this.language === 'en-US' ? Color.White : Color.Black)
.onClick(() => {
this.language = 'en-US';
});
}
}
// 保存按钮
Button('保存设置')
.width(200)
.height(40)
.backgroundColor('#007DFF')
.fontColor(Color.White)
.onClick(() => {
this.saveSettings();
});
}
.width('100%')
.height('100%')
.padding(24)
.justifyContent(FlexAlign.Start);
}
}
3.2 关系型数据库:待办事项管理
3.2.1 功能需求
实现一个待办事项列表,支持添加、删除、修改、查询待办事项
3.2.2 实现步骤
ets
// 定义待办事项数据结构
interface TodoItem {
id: number;
content: string;
completed: boolean;
createTime: string;
updateTime: string;
}
// 待办事项页面
@Entry
@Component
struct TodoListPage {
@State todoList: TodoItem[] = [];
@State inputContent: string = '';
// 数据库实例
private database: relationalStore.RdbStore | null = null;
// 页面初始化
async onPageShow() {
await this.initDatabase();
await this.loadTodoList();
}
// 初始化数据库
async initDatabase() {
const context = getContext(this) as common.UIAbilityContext;
// 数据库配置
const config: relationalStore.StoreConfig = {
name: 'todo_db.db',
securityLevel: relationalStore.SecurityLevel.S1,
encrypt: false
};
try {
// 创建或打开数据库
this.database = await relationalStore.getRdbStore(context, config);
// 创建表
const createTableSql = `
CREATE TABLE IF NOT EXISTS todo (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
completed INTEGER NOT NULL DEFAULT 0,
createTime TEXT NOT NULL,
updateTime TEXT NOT NULL
)
`;
await this.database.executeSql(createTableSql, []);
console.log('数据库初始化成功');
} catch (error) {
console.error('数据库初始化失败:', error);
}
}
// 加载待办事项列表
async loadTodoList() {
if (this.database) {
try {
const resultSet = await this.database.querySql(
'SELECT * FROM todo ORDER BY createTime DESC',
[]
);
const todoList: TodoItem[] = [];
while (resultSet.goToNextRow()) {
todoList.push({
id: resultSet.getInt(resultSet.getColumnIndex('id')),
content: resultSet.getString(resultSet.getColumnIndex('content')),
completed: resultSet.getInt(resultSet.getColumnIndex('completed')) === 1,
createTime: resultSet.getString(resultSet.getColumnIndex('createTime')),
updateTime: resultSet.getString(resultSet.getColumnIndex('updateTime'))
});
}
resultSet.close();
this.todoList = todoList;
} catch (error) {
console.error('加载待办事项失败:', error);
}
}
}
// 添加待办事项
async addTodoItem() {
if (!this.inputContent.trim() || !this.database) return;
const newTodo: Partial<TodoItem> = {
content: this.inputContent.trim(),
completed: false,
createTime: new Date().toISOString(),
updateTime: new Date().toISOString()
};
try {
// 插入数据
await this.database.insertWithValueBucket(
'todo',
relationalStore.valuesBucket(newTodo)
);
// 清空输入框
this.inputContent = '';
// 重新加载列表
await this.loadTodoList();
prompt.showToast({ message: '添加成功' });
} catch (error) {
console.error('添加待办事项失败:', error);
prompt.showToast({ message: '添加失败' });
}
}
// 更新待办事项状态
async updateTodoStatus(id: number, completed: boolean) {
if (!this.database) return;
try {
// 更新数据
await this.database.updateWithValueBucket(
'todo',
relationalStore.valuesBucket({
completed: completed ? 1 : 0,
updateTime: new Date().toISOString()
}),
relationalStore.RdbPredicates.create('todo').equalTo('id', id)
);
// 重新加载列表
await this.loadTodoList();
} catch (error) {
console.error('更新待办事项状态失败:', error);
}
}
// 删除待办事项
async deleteTodoItem(id: number) {
if (!this.database) return;
try {
// 删除数据
await this.database.delete(
relationalStore.RdbPredicates.create('todo').equalTo('id', id)
);
// 重新加载列表
await this.loadTodoList();
prompt.showToast({ message: '删除成功' });
} catch (error) {
console.error('删除待办事项失败:', error);
prompt.showToast({ message: '删除失败' });
}
}
build() {
Column({ space: 16 }) {
Text('待办事项')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 32 });
// 输入框与添加按钮
Row({ space: 12 }) {
TextInput({ placeholder: '输入待办事项' })
.width(240)
.height(40)
.backgroundColor(Color.White)
.onChange((value) => {
this.inputContent = value;
});
Button('添加')
.width(80)
.height(40)
.backgroundColor('#007DFF')
.fontColor(Color.White)
.onClick(() => {
this.addTodoItem();
});
}
.padding({ left: 24, right: 24 });
// 待办事项列表
List() {
ForEach(this.todoList, (item: TodoItem) => {
ListItem() {
Row({ space: 16 }) {
// 完成状态切换
Checkbox()
.checked(item.completed)
.onChange((isChecked) => {
this.updateTodoStatus(item.id, isChecked);
});
// 待办内容
Text(item.content)
.fontSize(16)
.textDecoration({
type: item.completed ? TextDecorationType.LineThrough : TextDecorationType.None
})
.fontColor(item.completed ? '#9CA3AF' : '#111827');
// 删除按钮
Button('删除')
.width(60)
.height(32)
.backgroundColor('#EF4444')
.fontColor(Color.White)
.fontSize(12)
.onClick(() => {
this.deleteTodoItem(item.id);
});
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8);
}
}, (item: TodoItem) => item.id);
}
.width('100%')
.height('60%')
.padding({ left: 24, right: 24 });
}
.width('100%')
.height('100%')
.backgroundColor('#F3F4F6');
}
}
3.3 文件存储:用户头像管理
3.3.1 功能需求
实现用户头像的选择、保存与加载
3.3.2 实现步骤
ets
// 用户头像页面
@Entry
@Component
struct AvatarPage {
@State avatarUri: string = '';
// 文件管理实例
private fileManager: fileio.FileManager | null = null;
onPageShow() {
// 初始化文件管理
this.fileManager = fileio.getFileManager();
}
// 选择并保存头像
async selectAvatar() {
try {
// 打开文件选择器
const result = await picker.select({
mimeType: ['image/*'],
count: 1
});
if (result && result.uri) {
// 保存头像到应用沙箱
const context = getContext(this) as common.UIAbilityContext;
const cacheDir = await context.getCacheDir();
// 生成唯一文件名
const fileName = `avatar_${Date.now()}.png`;
const targetPath = `${cacheDir}/${fileName}`;
// 复制文件
await this.fileManager?.copyFile({
srcUri: result.uri[0],
dstPath: targetPath
});
// 保存路径到Preferences
const preferences = await context.createPreferences('user_info', PreferencesConstant.Mode.MULTI_PROCESS);
await preferences.put('avatar_path', targetPath);
await preferences.flushSync();
// 更新UI
this.avatarUri = targetPath;
prompt.showToast({ message: '头像保存成功' });
}
} catch (error) {
console.error('选择头像失败:', error);
prompt.showToast({ message: '选择头像失败' });
}
}
build() {
Column({ space: 24 }) {
Text('用户头像')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 32 });
// 头像显示
Image(this.avatarUri || $r('app.media.default_avatar'))
.width(120)
.height(120)
.borderRadius(60)
.objectFit(ImageFit.Cover)
.margin({ top: 24 });
// 选择头像按钮
Button('选择头像')
.width(200)
.height(40)
.backgroundColor('#007DFF')
.fontColor(Color.White)
.onClick(() => {
this.selectAvatar();
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
.padding(24);
}
}
四、云同步实战☁️
4.1 功能需求
实现待办事项的云同步,支持多设备数据共享
4.2 实现步骤
ets
// 云同步配置
const CLOUD_DB_NAME = 'todo_cloud_db';
const CLOUD_TABLE_NAME = 'TodoItem';
// 待办事项云同步页面
@Entry
@Component
struct TodoCloudPage {
@State todoList: TodoItem[] = [];
@State isSyncing: boolean = false;
// 云数据库实例
private cloudDB: CloudDBZone | null = null;
// 页面初始化
async onPageShow() {
await this.initCloudDB();
await this.syncData();
}
// 初始化云数据库
async initCloudDB() {
try {
// 初始化AGC SDK
await AGConnectCloudDB.initialize();
// 创建云数据库实例
const cloudDBConfig = {
zoneName: CLOUD_DB_NAME,
enableAutoSync: true
};
this.cloudDB = await AGConnectCloudDB.openCloudDBZone(cloudDBConfig);
console.log('云数据库初始化成功');
} catch (error) {
console.error('云数据库初始化失败:', error);
}
}
// 数据同步
async syncData() {
if (!this.cloudDB) return;
this.isSyncing = true;
try {
// 从云端下载数据
const cloudItems = await this.cloudDB.query({
table: CLOUD_TABLE_NAME,
query: AGConnectCloudDB.query().orderBy('createTime', 'desc')
});
// 与本地数据合并
await this.mergeData(cloudItems as TodoItem[]);
// 上传本地新增数据到云端
await this.uploadLocalData();
prompt.showToast({ message: '数据同步成功' });
} catch (error) {
console.error('数据同步失败:', error);
prompt.showToast({ message: '数据同步失败' });
} finally {
this.isSyncing = false;
}
}
// 合并数据
async mergeData(cloudItems: TodoItem[]) {
// 实现数据合并逻辑,解决冲突
// 这里简化处理,以云端数据为准
this.todoList = cloudItems;
}
// 上传本地数据
async uploadLocalData() {
// 实现本地数据上传逻辑
}
build() {
Column({ space: 24 }) {
// 顶部导航栏
Row({ space: 12 }) {
Text('待办事项(云同步)')
.fontSize(24)
.fontWeight(FontWeight.Bold);
Button(this.isSyncing ? '同步中...' : '同步数据')
.width(120)
.height(40)
.backgroundColor(this.isSyncing ? '#9CA3AF' : '#007DFF')
.fontColor(Color.White)
.onClick(() => {
if (!this.isSyncing) {
this.syncData();
}
});
}
.margin({ top: 32 });
// 待办事项列表
List() {
ForEach(this.todoList, (item: TodoItem) => {
ListItem() {
Row({ space: 16 }) {
Checkbox()
.checked(item.completed)
.onChange(async (isChecked) => {
// 更新本地与云端数据
item.completed = isChecked;
if (this.cloudDB) {
await this.cloudDB.update({
table: CLOUD_TABLE_NAME,
data: item
});
}
});
Text(item.content)
.fontSize(16);
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8);
}
}, (item: TodoItem) => item.id);
}
.width('100%')
.height('60%')
.padding({ left: 24, right: 24 });
}
.width('100%')
.height('100%')
.backgroundColor('#F3F4F6');
}
}
五、数据管理系统整合📊
5.1 整合架构
数据管理系统
├─ 本地存储层
│ ├─ Preferences:用户配置
│ ├─ 关系型数据库:核心业务数据
│ └─ 文件存储:多媒体文件
├─ 云同步层
│ ├─ 云数据库:数据持久化
│ └─ 同步服务:双向数据同步
└─ 业务逻辑层
├─ 数据合并与冲突处理
└─ 数据访问接口
5.2 最佳实践
- 数据分层:将本地与云端数据分离,确保数据一致性
- 冲突处理:采用"最后修改时间"或"用户选择"策略解决数据冲突
- 性能优化:减少不必要的同步请求,使用批量操作提高效率
- 安全保障:对敏感数据进行加密存储,限制云同步权限
六、常见问题与优化方案⚠️
6.1 Preferences 数据丢失
问题 :应用卸载后数据丢失
优化方案:将重要数据存储到关系型数据库或文件存储中
6.2 数据库性能问题
问题 :大数据量查询缓慢
优化方案:
- 为查询字段创建索引
- 使用分页查询减少单次查询数据量
- 避免在主线程执行复杂查询
6.3 云同步冲突
问题 :多设备同步时数据冲突
优化方案:
- 实现冲突检测机制
- 提供用户手动解决冲突的界面
- 使用版本号或时间戳管理数据版本
6.4 文件存储权限问题
问题 :无法读取/写入文件
优化方案:
- 在
config.json中声明文件存储权限 - 动态请求用户授权
- 使用应用沙箱目录存储文件
七、总结与拓展✅
7.1 本章总结
通过本章学习,我们掌握了:
- 鸿蒙数据持久化的核心方案与应用场景
- Preferences、关系型数据库、文件存储的具体实现
- 云同步机制与数据一致性保障
- 完整数据管理系统的构建流程
7.2 拓展练习
- 实现待办事项的云同步冲突处理
- 为用户头像添加上传到云存储的功能
- 实现应用退出后自动同步数据的功能
- 构建数据备份与恢复功能
7.3 进阶学习方向
- 鸿蒙分布式数据管理
- 数据加密与安全存储
- 大数据量处理与性能优化
- 跨平台数据同步方案
通过不断实践与拓展,你将逐步掌握鸿蒙数据持久化与云同步的核心技术,构建出稳定、高效的数据管理系统!
