Harmony学习之本地数据存储

Harmony学习之本地数据存储

一、场景引入

小明在上一篇文章中学会了网络请求,现在他需要将用户登录信息、应用配置、商品收藏等数据持久化保存到本地,这样即使应用重启或网络断开,用户也能看到自己的个性化设置和历史数据。本篇文章将系统讲解HarmonyOS的本地数据存储机制,帮助小明实现数据的本地持久化。

二、本地数据存储方案对比

HarmonyOS提供了多种本地数据存储方式,每种方式都有其适用场景:

存储方式 适用场景 特点
Preferences 用户配置、登录状态、开关设置 轻量级键值对存储,类似SharedPreferences
关系型数据库 结构化数据、复杂查询、事务支持 基于SQLite,支持ACID事务
文件存储 大文件、图片、文档 支持二进制流和文本操作

三、Preferences轻量级存储

1. 核心概念

Preferences是HarmonyOS提供的轻量级键值对存储方案,适合存储用户配置、应用设置等小数据量信息。数据以Key-Value形式存储,支持字符串、数字、布尔值等基本数据类型。

2. 基本使用

// src/main/ets/common/StorageUtil.ts
import preferences from '@ohos.data.preferences';

export class StorageUtil {
  private static instance: StorageUtil;
  private preferences: preferences.Preferences | null = null;

  // 获取Preferences实例
  public static async getInstance(): Promise<StorageUtil> {
    if (!StorageUtil.instance) {
      StorageUtil.instance = new StorageUtil();
      await StorageUtil.instance.init();
    }
    return StorageUtil.instance;
  }

  // 初始化Preferences
  private async init() {
    try {
      this.preferences = await preferences.getPreferences(
        globalThis.getContext(), 
        'app_config'
      );
    } catch (error) {
      console.error('初始化Preferences失败:', error);
    }
  }

  // 保存数据
  public async save(key: string, value: any): Promise<boolean> {
    if (!this.preferences) return false;
    
    try {
      await this.preferences.put(key, value);
      await this.preferences.flush(); // 持久化到文件
      return true;
    } catch (error) {
      console.error('保存数据失败:', error);
      return false;
    }
  }

  // 读取数据
  public async get<T>(key: string, defaultValue: T): Promise<T> {
    if (!this.preferences) return defaultValue;
    
    try {
      return await this.preferences.get(key, defaultValue);
    } catch (error) {
      console.error('读取数据失败:', error);
      return defaultValue;
    }
  }

  // 删除数据
  public async delete(key: string): Promise<boolean> {
    if (!this.preferences) return false;
    
    try {
      await this.preferences.delete(key);
      await this.preferences.flush();
      return true;
    } catch (error) {
      console.error('删除数据失败:', error);
      return false;
    }
  }
}

3. 实战应用:保存用户登录状态

// src/main/ets/pages/Login.ets
import { StorageUtil } from '../common/StorageUtil';

@Entry
@Component
struct Login {
  @State username: string = '';
  @State password: string = '';

  build() {
    Column({ space: 20 }) {
      // ... 登录表单UI代码
      Button('登录')
        .onClick(() => {
          this.handleLogin();
        })
    }
  }

  // 处理登录
  private async handleLogin() {
    // 验证用户名密码...
    
    // 保存登录状态
    const storage = await StorageUtil.getInstance();
    await storage.save('isLoggedIn', true);
    await storage.save('username', this.username);
    
    // 跳转到首页
    router.replaceUrl({ url: 'pages/Home' });
  }
}

四、关系型数据库存储

1. 核心概念

关系型数据库基于SQLite组件,提供结构化数据存储能力,支持复杂的查询、事务、索引等功能。适合存储用户信息、商品数据、订单记录等结构化数据。

2. 数据库创建与表定义

// src/main/ets/database/DatabaseHelper.ts
import relationalStore from '@ohos.data.relationalStore';

// 用户表定义
export interface User {
  id?: number;
  name: string;
  email: string;
  age: number;
  createTime: number;
}

export class DatabaseHelper {
  private static instance: DatabaseHelper;
  private rdbStore: relationalStore.RdbStore | null = null;

  // 数据库配置
  private readonly DB_NAME = 'app_database.db';
  private readonly DB_VERSION = 1;

  // 创建用户表SQL
  private readonly CREATE_USER_TABLE = `
    CREATE TABLE IF NOT EXISTS users (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      email TEXT UNIQUE NOT NULL,
      age INTEGER,
      createTime INTEGER DEFAULT (strftime('%s', 'now'))
    )
  `;

  // 获取单例实例
  public static async getInstance(): Promise<DatabaseHelper> {
    if (!DatabaseHelper.instance) {
      DatabaseHelper.instance = new DatabaseHelper();
      await DatabaseHelper.instance.init();
    }
    return DatabaseHelper.instance;
  }

  // 初始化数据库
  private async init() {
    try {
      const context = globalThis.getContext();
      const storeConfig: relationalStore.StoreConfig = {
        name: this.DB_NAME,
        securityLevel: relationalStore.SecurityLevel.S1
      };

      this.rdbStore = await relationalStore.getRdbStore(context, storeConfig);
      
      // 创建表
      await this.rdbStore.executeSql(this.CREATE_USER_TABLE);
      console.log('数据库初始化成功');
    } catch (error) {
      console.error('数据库初始化失败:', error);
    }
  }
}

3. 增删改查操作

// 在DatabaseHelper类中继续添加方法

// 插入用户
public async insertUser(user: User): Promise<number> {
  if (!this.rdbStore) return -1;
  
  try {
    const valueBucket: relationalStore.ValuesBucket = {
      'name': user.name,
      'email': user.email,
      'age': user.age
    };
    
    const rowId = await this.rdbStore.insert('users', valueBucket);
    return rowId;
  } catch (error) {
    console.error('插入用户失败:', error);
    return -1;
  }
}

// 查询用户
public async queryUsers(): Promise<User[]> {
  if (!this.rdbStore) return [];
  
  try {
    const predicates = new relationalStore.RdbPredicates('users');
    const columns = ['id', 'name', 'email', 'age', 'createTime'];
    
    const resultSet = await this.rdbStore.query(predicates, columns);
    const users: User[] = [];
    
    while (resultSet.goToNextRow()) {
      users.push({
        id: resultSet.getLong(resultSet.getColumnIndex('id')),
        name: resultSet.getString(resultSet.getColumnIndex('name')),
        email: resultSet.getString(resultSet.getColumnIndex('email')),
        age: resultSet.getLong(resultSet.getColumnIndex('age')),
        createTime: resultSet.getLong(resultSet.getColumnIndex('createTime'))
      });
    }
    
    resultSet.close();
    return users;
  } catch (error) {
    console.error('查询用户失败:', error);
    return [];
  }
}

// 更新用户
public async updateUser(user: User): Promise<boolean> {
  if (!this.rdbStore || !user.id) return false;
  
  try {
    const predicates = new relationalStore.RdbPredicates('users');
    predicates.equalTo('id', user.id);
    
    const valueBucket: relationalStore.ValuesBucket = {
      'name': user.name,
      'email': user.email,
      'age': user.age
    };
    
    const affectedRows = await this.rdbStore.update(valueBucket, predicates);
    return affectedRows > 0;
  } catch (error) {
    console.error('更新用户失败:', error);
    return false;
  }
}

// 删除用户
public async deleteUser(id: number): Promise<boolean> {
  if (!this.rdbStore) return false;
  
  try {
    const predicates = new relationalStore.RdbPredicates('users');
    predicates.equalTo('id', id);
    
    const affectedRows = await this.rdbStore.delete(predicates);
    return affectedRows > 0;
  } catch (error) {
    console.error('删除用户失败:', error);
    return false;
  }
}

4. 实战应用:用户管理

// src/main/ets/pages/UserList.ets
import { DatabaseHelper, User } from '../database/DatabaseHelper';

@Entry
@Component
struct UserList {
  @State users: User[] = [];

  aboutToAppear() {
    this.loadUsers();
  }

  build() {
    Column() {
      List() {
        ForEach(this.users, (user: User) => {
          ListItem() {
            Column() {
              Text(user.name)
                .fontSize(18)
              Text(user.email)
                .fontSize(14)
                .fontColor(Color.Gray)
            }
            .padding(10)
          }
        })
      }
      .layoutWeight(1)
    }
  }

  // 加载用户列表
  private async loadUsers() {
    const dbHelper = await DatabaseHelper.getInstance();
    this.users = await dbHelper.queryUsers();
  }
}

五、文件存储

1. 核心概念

文件存储适用于保存图片、文档、大体积数据等二进制内容。HarmonyOS提供了文件系统API,支持文件的创建、读写、删除等操作。

2. 文件操作示例

// src/main/ets/common/FileUtil.ts
import fs from '@ohos.file.fs';

export class FileUtil {
  // 写入文件
  public static async writeFile(filePath: string, content: string): Promise<boolean> {
    try {
      const file = await fs.open(filePath, fs.OpenMode.CREATE | fs.OpenMode.WRITE);
      await fs.write(file.fd, content);
      await fs.close(file.fd);
      return true;
    } catch (error) {
      console.error('写入文件失败:', error);
      return false;
    }
  }

  // 读取文件
  public static async readFile(filePath: string): Promise<string> {
    try {
      const file = await fs.open(filePath, fs.OpenMode.READ_ONLY);
      const stat = await fs.stat(filePath);
      const buffer = new ArrayBuffer(stat.size);
      await fs.read(file.fd, buffer);
      await fs.close(file.fd);
      
      const textDecoder = new util.TextDecoder();
      return textDecoder.decode(buffer);
    } catch (error) {
      console.error('读取文件失败:', error);
      return '';
    }
  }

  // 获取应用私有目录
  public static getFilesDir(): string {
    const context = globalThis.getContext();
    return context.filesDir;
  }
}

六、最佳实践

1. 数据存储选型指南

Preferences适用场景

  • 用户配置信息(主题、语言、字体大小)
  • 登录状态、Token等敏感信息
  • 简单的开关设置

关系型数据库适用场景

  • 用户信息、商品列表等结构化数据
  • 需要复杂查询、排序、分页的场景
  • 需要事务支持的业务数据

文件存储适用场景

  • 图片、视频、文档等大文件
  • 日志文件、缓存数据
  • 需要导出/导入的数据

2. 性能优化建议

Preferences优化

  • 批量操作时先put再flush,减少IO次数
  • 避免存储大JSON对象,建议使用数据库
  • 及时清理不再使用的数据

数据库优化

  • 为常用查询字段创建索引
  • 使用事务处理批量操作
  • 避免在主线程执行耗时查询

文件存储优化

  • 大文件使用流式读写
  • 定期清理缓存文件
  • 使用临时目录存储临时文件

3. 错误处理与调试

// 完整的错误处理示例
try {
  const dbHelper = await DatabaseHelper.getInstance();
  const users = await dbHelper.queryUsers();
  // 处理数据...
} catch (error) {
  console.error('数据库操作失败:', error);
  promptAction.showToast({
    message: '数据加载失败,请稍后重试',
    duration: 2000
  });
}

七、总结与行动建议

核心要点回顾

  1. Preferences:轻量级键值对存储,适合配置信息
  2. 关系型数据库:结构化数据存储,支持复杂查询
  3. 文件存储:大文件、二进制数据存储
  4. 选型原则:根据数据特点和业务需求选择合适的存储方案

行动建议

  1. 动手实践:创建用户配置页面,使用Preferences保存主题设置
  2. 数据库实战:实现一个简单的待办事项应用,使用数据库存储任务数据
  3. 文件操作:实现图片缓存功能,将网络图片保存到本地
  4. 性能优化:为数据库查询添加索引,优化查询性能

通过本篇文章的学习,你已经掌握了HarmonyOS本地数据存储的核心能力。下一篇文章将深入讲解列表渲染与性能优化,帮助你实现高效的数据展示和交互体验。

posted @ 2025-12-23 23:17  J_____P  阅读(0)  评论(0)    收藏  举报