鸿蒙数据持久化实战:构建本地存储与云同步系统

鸿蒙数据持久化实战:构建本地存储与云同步系统

一、章节概述

学习目标

  1. 掌握鸿蒙数据持久化的核心方案与应用场景
  2. 熟练使用 Preferences、关系型数据库、文件存储实现本地数据管理
  3. 理解并应用鸿蒙云同步机制构建全场景数据系统
  4. 实现本地与云端的数据双向同步与冲突处理
  5. 构建完整的数据持久化与同步解决方案

💡 重点内容

本地存储方案选型、数据库CRUD操作、文件存储权限管理、云同步机制、数据一致性保障

⚠️ 前置基础

已掌握鸿蒙 ArkTS 组件化开发、页面交互、状态管理等核心知识


二、鸿蒙数据持久化核心体系🔧

2.1 持久化方案分类与应用场景

鸿蒙提供了多层次的数据持久化方案,满足不同业务需求:

方案类型 核心特性 应用场景
📋 Preferences 轻量级键值对存储、高性能查询 用户设置、应用配置、临时数据缓存
🗄️ 关系型数据库 结构化存储、事务支持、复杂查询 业务核心数据(如订单、待办、联系人)
📁 文件存储 无结构存储、支持大文件 图片、音频、视频、文档等多媒体文件

2.2 核心概念

  1. 数据上下文AbilityContext/ApplicationContext 提供持久化操作的入口
  2. 事务:确保数据库操作的原子性、一致性、隔离性、持久性(ACID)
  3. 云同步:基于华为账号系统实现本地与云端数据的双向同步

三、本地存储实战⌨️

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 最佳实践

  1. 数据分层:将本地与云端数据分离,确保数据一致性
  2. 冲突处理:采用"最后修改时间"或"用户选择"策略解决数据冲突
  3. 性能优化:减少不必要的同步请求,使用批量操作提高效率
  4. 安全保障:对敏感数据进行加密存储,限制云同步权限

六、常见问题与优化方案⚠️

6.1 Preferences 数据丢失

问题 :应用卸载后数据丢失
优化方案:将重要数据存储到关系型数据库或文件存储中

6.2 数据库性能问题

问题 :大数据量查询缓慢
优化方案

  • 为查询字段创建索引
  • 使用分页查询减少单次查询数据量
  • 避免在主线程执行复杂查询

6.3 云同步冲突

问题 :多设备同步时数据冲突
优化方案

  • 实现冲突检测机制
  • 提供用户手动解决冲突的界面
  • 使用版本号或时间戳管理数据版本

6.4 文件存储权限问题

问题 :无法读取/写入文件
优化方案

  • config.json 中声明文件存储权限
  • 动态请求用户授权
  • 使用应用沙箱目录存储文件

七、总结与拓展✅

7.1 本章总结

通过本章学习,我们掌握了:

  1. 鸿蒙数据持久化的核心方案与应用场景
  2. Preferences、关系型数据库、文件存储的具体实现
  3. 云同步机制与数据一致性保障
  4. 完整数据管理系统的构建流程

7.2 拓展练习

  1. 实现待办事项的云同步冲突处理
  2. 为用户头像添加上传到云存储的功能
  3. 实现应用退出后自动同步数据的功能
  4. 构建数据备份与恢复功能

7.3 进阶学习方向

  1. 鸿蒙分布式数据管理
  2. 数据加密与安全存储
  3. 大数据量处理与性能优化
  4. 跨平台数据同步方案

通过不断实践与拓展,你将逐步掌握鸿蒙数据持久化与云同步的核心技术,构建出稳定、高效的数据管理系统!

相关推荐
wjs20242 小时前
《Ionic 侧栏菜单》
开发语言
祁思妙想2 小时前
linux常用命令
开发语言·python
IMPYLH2 小时前
Lua 的 IO (输入/输出)模块
开发语言·笔记·后端·lua
普通网友2 小时前
Objective-C 类的方法重载与重写:区别与正确使用场景
开发语言·ios·objective-c
喵了meme2 小时前
C语言实战6
c语言·开发语言
AAA阿giao2 小时前
从“操纵绳子“到“指挥木偶“:Vue3 Composition API 如何彻底改变前端开发范式
开发语言·前端·javascript·vue.js·前端框架·vue3·compositionapi
小裴(碎碎念版)2 小时前
文件读写常用操作
开发语言·爬虫·python
sheji34163 小时前
【开题答辩全过程】以 基于Java的应急安全学习平台的设计与实现为例,包含答辩的问题和答案
java·开发语言·学习