HarmonyOS Preferences 与关系型数据库开发

发布时间:2026/8/1 12:57:18
HarmonyOS Preferences 与关系型数据库开发 HarmonyOS为开发者提供了层次清晰的数据持久化方案其中用户首选项Preferences与关系型数据库RelationalStore是处理本地数据存储的两大核心工具。前者如同轻便的“记忆便签”适合存储用户的个性化设置后者则像一个功能强大的“数据管家”专门管理结构复杂、关系紧密的业务数据。下面为你详细解析它们的核心机制、开发实践与选型要点。一、 用户首选项轻量级数据的“记忆便签”用户首选项为应用提供了轻量级的键值对Key-Value数据读写能力。它非常适合存储小体量的配置信息例如用户的字体大小偏好、主题模式深色/浅色、是否首次启动等标志位。核心机制用户首选项的数据会以文本文件的形式保存在设备中。当应用访问时会将文件中的数据全量加载到内存中因此其读写速度极快但也正因如此它不适合存储大量或复杂的数据数据量建议保持在KB级别。1. 开发实战以“字体大小调节”为例本示例将展示如何通过用户首选项保存并读取用户设置的字体大小偏移量。步骤一封装工具类创建一个PreferencesUtil.ets文件用于集中管理首选项的操作。typescriptimport { preferences } from kit.ArkData; import { BusinessError } from kit.BasicServicesKit; // 定义存储实例的名称和Key const PREFERENCES_NAME: string MyAppPreferences; const KEY_FONT_SIZE: string fontSizeOffset; export class PreferencesUtil { private preference?: preferences.Preferences; // 1. 获取首选项实例 async getPreferencesInstance(context: Context) { if (!this.preference) { let options: preferences.Options { name: PREFERENCES_NAME }; try { // 通过上下文获取Preferences实例 this.preference await preferences.getPreferences(context, options); } catch (err) { let code (err as BusinessError).code; console.error(获取Preferences实例失败错误码: ${code}); } } return this.preference; } // 2. 保存数据写入并持久化 async saveFontSize(context: Context, fontSize: number) { let pref await this.getPreferencesInstance(context); if (pref) { try { // 写入数据 await pref.put(KEY_FONT_SIZE, fontSize); // 重要调用flush()方法将数据从内存持久化到磁盘文件 await pref.flush(); console.info(字体大小已保存: ${fontSize}); } catch (err) { console.error(保存字体大小失败: ${(err as BusinessError).code}); } } } // 3. 读取数据若Key不存在返回默认值 async getFontSize(context: Context): Promisenumber { let pref await this.getPreferencesInstance(context); let defaultValue: number 0; // 默认偏移量为0 if (pref) { try { let result await pref.get(KEY_FONT_SIZE, defaultValue); return result as number; } catch (err) { console.error(读取字体大小失败: ${(err as BusinessError).code}); return defaultValue; } } return defaultValue; } }步骤二在页面中使用typescriptimport { PreferencesUtil } from ../database/PreferencesUtil; Entry Component struct SettingsPage { State fontSizeOffset: number 0; private prefUtil: PreferencesUtil new PreferencesUtil(); async aboutToAppear() { // 页面加载前读取存储的字体偏移量 this.fontSizeOffset await this.prefUtil.getFontSize(getContext(this)); } build() { Column() { Text(Hello HarmonyOS) .fontSize(16 this.fontSizeOffset) // 系统默认大小偏移量 .fontWeight(FontWeight.Bold) Button(增加字体) .onClick(async () { this.fontSizeOffset 2; // 保存新的偏移量 await this.prefUtil.saveFontSize(getContext(this), this.fontSizeOffset); }) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) } }关键技术点flush()写入数据后务必调用flush()否则数据仅存在于内存中应用退出后会丢失。默认值在get()方法中设置合理的默认值可以避免因数据不存在而导致的异常。二、 关系型数据库结构化数据的“全能管家”关系型数据库RelationalStore底层基于SQLite提供了一套完整的SQL接口支持事务、索引、多表关联查询等高级功能。它适用于管理大量、结构化的业务数据如记账应用的账单、聊天记录、商品列表等。1. 开发实战以“运动计划管理”为例此案例包含“运动表”sport和“计划表”plan演示了从建表到联表查询的完整流程。步骤一初始化数据库并建表typescript// RDBStoreUtil.ets import { relationalStore } from kit.ArkData; import { BusinessError } from kit.BasicServicesKit; const DB_NAME: string Objective.db; const CREATE_SPORT_TABLE: string CREATE TABLE IF NOT EXISTS sport ( id INTEGER PRIMARY KEY AUTOINCREMENT, sport_name TEXT NOT NULL, sport_type TEXT );; const CREATE_PLAN_TABLE: string CREATE TABLE IF NOT EXISTS plan ( id INTEGER PRIMARY KEY AUTOINCREMENT, sport_id INTEGER, duration INTEGER, status INTEGER, FOREIGN KEY (sport_id) REFERENCES sport(id) );; export class RDBStoreUtil { private rdbStore?: relationalStore.RdbStore; async initRdbStore(context: Context) { if (this.rdbStore) { return this.rdbStore; } const config: relationalStore.StoreConfig { name: DB_NAME, securityLevel: relationalStore.SecurityLevel.S1 // 设置安全级别 }; try { this.rdbStore await relationalStore.getRdbStore(context, config); // 执行建表语句 await this.rdbStore.executeSql(CREATE_SPORT_TABLE); await this.rdbStore.executeSql(CREATE_PLAN_TABLE); console.info(数据库初始化及建表成功); return this.rdbStore; } catch (err) { console.error(初始化数据库失败: ${(err as BusinessError).code}); } } // ... 后续CRUD方法 }步骤二实现数据的增删改查typescript// 1. 插入数据使用ValuesBucket封装 async insertPlan(sportId: number, duration: number) { if (!this.rdbStore) return -1; const valueBucket: relationalStore.ValuesBucket { sport_id: sportId, duration: duration, status: 0 // 0表示未完成 }; try { let rowId await this.rdbStore.insert(plan, valueBucket); console.info(插入计划成功rowId: ${rowId}); return rowId; } catch (err) { console.error(插入失败: ${(err as BusinessError).code}); return -1; } } // 2. 联表查询查询所有计划及其对应的运动名称 async queryAllPlansWithSport(): PromiseArrayobject { if (!this.rdbStore) return []; const sql SELECT plan.id, plan.duration, plan.status, sport.sport_name, sport.sport_type FROM plan INNER JOIN sport ON plan.sport_id sport.id; let resultSet: relationalStore.ResultSet | undefined; try { // 执行查询SQL resultSet await this.rdbStore.querySql(sql); let resultArray: Arrayobject []; // 遍历结果集 while (resultSet.goToNextRow()) { let id resultSet.getLong(resultSet.getColumnIndex(id)); let duration resultSet.getLong(resultSet.getColumnIndex(duration)); let sportName resultSet.getString(resultSet.getColumnIndex(sport_name)); resultArray.push({ id, duration, sportName }); } return resultArray; } catch (err) { console.error(联表查询失败: ${(err as BusinessError).code}); return []; } finally { // 关键必须关闭ResultSet以释放内存 resultSet?.close(); } }关键技术点ResultSet管理查询返回的ResultSet对象在使用完毕后必须调用close()方法释放资源否则会造成内存泄漏。事务对于批量插入或更新等需要保持数据一致性的操作应使用beginTransaction()、commit()和rollback()来管理事务。三、 如何选择一张表看清差异特性维度用户首选项 (Preferences)关系型数据库 (RelationalStore)数据模型键值对 (Key-Value)二维表 (行与列)适用数据量小(KB级别)大(无严格上限建议单条2MB)查询能力简单仅能通过Key查询强大支持复杂SQL、索引、联表查询典型场景用户设置、主题、登录Token、应用配置记账记录、通讯录、商品订单、聊天消息事务支持不支持完整支持(ACID)性能特点全量加载到内存读取速度极快按需查询支持分页和索引优化总结用户首选项是轻量级存储的首选其简单、快速的特点使其成为管理应用“状态”和“偏好”的最佳工具。牢记使用flush()确保持久化。关系型数据库则是处理复杂业务逻辑和海量数据的中坚力量。它提供的SQL能力、事务机制和数据结构化能力能满足绝大多数应用的核心数据管理需求。开发时注意合理使用事务和及时释放ResultSet资源。