鸿蒙数据持久化实战:构建本地存储与云同步系统
一、章节概述
✅学习目标
- 掌握鸿蒙数据持久化的核心方案与应用场景
- 熟练使用 Preferences、关系型数据库、文件存储实现本地数据管理
- 理解并应用鸿蒙云同步机制构建全场景数据系统
- 实现本地与云端的数据双向同步与冲突处理
- 构建完整的数据持久化与同步解决方案
💡重点内容
本地存储方案选型、数据库CRUD操作、文件存储权限管理、云同步机制、数据一致性保障
⚠️前置基础
已掌握鸿蒙 ArkTS 组件化开发、页面交互、状态管理等核心知识
二、鸿蒙数据持久化核心体系🔧
2.1 持久化方案分类与应用场景
鸿蒙提供了多层次的数据持久化方案,满足不同业务需求:
| 方案类型 | 核心特性 | 应用场景 |
|---|---|---|
| 📋 Preferences | 轻量级键值对存储、高性能查询 | 用户设置、应用配置、临时数据缓存 |
| 🗄️ 关系型数据库 | 结构化存储、事务支持、复杂查询 | 业务核心数据(如订单、待办、联系人) |
| 📁 文件存储 | 无结构存储、支持大文件 | 图片、音频、视频、文档等多媒体文件 |
2.2 核心概念
- 数据上下文:
AbilityContext/ApplicationContext提供持久化操作的入口 - 事务:确保数据库操作的原子性、一致性、隔离性、持久性(ACID)
- 云同步:基于华为账号系统实现本地与云端数据的双向同步
三、本地存储实战⌨️
3.1 Preferences:用户设置存储
3.1.1 功能需求
构建用户设置页面,保存主题偏好(深色/浅色)和语言设置(中文/英文)
3.1.2 实现步骤
// 定义常量与类型 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 实现步骤
// 定义待办事项数据结构 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 实现步骤
// 用户头像页面 @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 实现步骤
// 云同步配置 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 进阶学习方向
- 鸿蒙分布式数据管理
- 数据加密与安全存储
- 大数据量处理与性能优化
- 跨平台数据同步方案
通过不断实践与拓展,你将逐步掌握鸿蒙数据持久化与云同步的核心技术,构建出稳定、高效的数据管理系统!