作为一个每天和鸿蒙应用打交道的开发者,我深知 “数据持久化” 有多重要 —— 总不能每次打开 APP,用户都得重新设置偏好、重新加载所有数据吧?

上节给大家介绍了从网络获取数据的课程,接下来跟大家聊聊这门课的核心内容、实用价值,还有我自己的学习感受。

课程链接:<HarmonyOS第一课>保存应用数据

一、课程核心内容拆解

这门课把 “保存应用数据” 拆成了几个梯度分明的模块,从最基础的 “用户习惯记忆”,到 “结构化数据管理”,再到 “实战落地”,每一块都能解决实际开发里的 “数据留存” 痛点。

1. 用户首选项的使用

“用户首选项” 是鸿蒙里存轻量键值对数据的工具,像用户的 “深色 / 浅色主题偏好”“默认字体大小”,甚至 “上次浏览到哪篇文章”,都能靠它实现 “记忆”。

用户首选项运作机制:
在这里插入图片描述
课程从 “基础使用” 开始,一步步带练:先通过Preferences.getPreferences(context, name)获取首选项实例,相当于 “打开记忆小本本”;接着用put方法存数据(支持布尔、字符串、数字等类型),比如把 “用户是否开启通知” 存为布尔值;再用get方法取数据,还能设置默认值(比如用户没设置过主题,就默认给个浅色主题),防止程序因为 “数据不存在” 崩溃。

“实现数据持久化” 这部分,还重点讲了 “数据提交”—— 因为首选项是异步缓存的,必须调用flush()方法,才能确保数据真正存到本地文件里。

dataPreferences.flush((err: BusinessError) => {
  if (err) {
    console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
    return;
  }
  console.info('Succeeded in flushing.');
})

这步看似微小,却能避免 “用户刚设置完,APP 突然崩溃导致配置丢失” 的尴尬,课程把这种 “防呆细节” 讲透了,对新手特别友好。

2. 关系型数据库的使用

如果应用要存复杂且关联的数据(比如记账 APP 的 “账单表”“分类表”,社交 APP 的 “用户表”“好友关系表”),关系型数据库就是核心工具。鸿蒙的关系型数据库基于 SQLite 封装,课程从 “创建数据库与表结构” 教起:用RelationalStore创建数据库,通过DataAbilityHelper执行 SQL 语句建表。接着详细讲解 “增删查改” 四大操作:

2.1 新增数据

把用户信息封装成ValuesBucket对象,插入到表中;

let value1 = 'Lisa';
let value2 = 18;
let value3 = 100.5;
let value4 = new Uint8Array([1, 2, 3, 4, 5]);
let value5 = BigInt('15822401018187971961171');
const valueBucket: relationalStore.ValuesBucket = {
  NAME: value1,
  AGE: value2,
  SALARY: value3,
  CODES: value4,
  IDENTITY: value5,
};

if (store !== undefined) {
  try {
    const rowId = await store.insert('EMPLOYEE', valueBucket);
    console.info(`Succeeded in inserting data. rowId:${rowId}`);
  } catch (error) {
    const err = error as BusinessError;
    console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`);
  }
}
2.2 查询数据

调用query()方法查找数据,返回一个ResultSet结果集。;

let predicates2 = new relationalStore.RdbPredicates('EMPLOYEE');
predicates2.equalTo('NAME', 'Rose');
if (store !== undefined) {
  (store as relationalStore.RdbStore).query(predicates2, ['ID', 'NAME', 'AGE', 'SALARY', 'IDENTITY'], (err: BusinessError, resultSet) => {
    if (err) {
      console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
    // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
    while (resultSet.goToNextRow()) {
      const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
      const name = resultSet.getString(resultSet.getColumnIndex('NAME'));
      const age = resultSet.getLong(resultSet.getColumnIndex('AGE'));
      const salary = resultSet.getDouble(resultSet.getColumnIndex('SALARY'));
      const identity = resultSet.getValue(resultSet.getColumnIndex('IDENTITY'));
      console.info(`id=${id}, name=${name}, age=${age}, salary=${salary}, identity=${identity}`);
    }
    // 释放数据集的内存
    resultSet.close();
  })
}
2.3 修改 / 删除数据

通过 SQL 或封装方法,更新特定条件的数据,或删除冗余记录。

if (store !== undefined) {
  // 创建事务对象
  try {
    const transaction = await store.createTransaction();
    try {
      // 使用事务对象插入数据
      const rowId = await transaction.insert(
        'EMPLOYEE',
        {
          NAME: 'Lisa',
          AGE: 18,
          SALARY: 100.5,
          CODES: new Uint8Array([1, 2, 3, 4, 5])
        },
        relationalStore.ConflictResolution.ON_CONFLICT_REPLACE
      );
      console.info(`Insert is successful, rowId = ${rowId}`);

      const predicates = new relationalStore.RdbPredicates('EMPLOYEE');
      predicates.equalTo('NAME', 'Lisa');
      // 使用事务对象更新数据
      const rows = await transaction.update(
        {
          NAME: 'Rose',
          AGE: 22,
          SALARY: 200.5,
          CODES: new Uint8Array([1, 2, 3, 4, 5])
        },
        predicates,
        relationalStore.ConflictResolution.ON_CONFLICT_REPLACE
      );
      console.info(`Updated row count: ${rows}`);

      // 使用事务对象删除数据
      await transaction.execute('DELETE FROM EMPLOYEE WHERE age = ? OR age = ?', [21, 20]);
      console.info(`execute delete success`);

      // 提交事务
      await transaction.commit();
      console.info('Transaction commit success.');
    } catch (error) {
      const err = error as BusinessError;
      // 执行失败回滚事务
      await transaction.rollback();
      console.error(`Transaction execute failed, code is ${err.code}, message is ${err.message}`);
    }
  } catch (error) {
    const err = error as BusinessError;
    console.error(`createTransaction failed, code is ${err.code}, message is ${err.message}`);
  }
}

课程还特意对比了 “用户首选项” 和 “关系型数据库” 的适用场景:首选项适合存零散的 “配置项”,数据库适合存 “成体系的业务数据”。这让我瞬间清晰了技术选型逻辑 —— 就像家里的小物件(钥匙、耳机)可以放桌面收纳盒(首选项),但大量书籍、文件就得放书柜(数据库)。

我跟着做了个 “简易记账本” 案例,建了 “账单表” 存消费金额、类型、日期,然后用查询功能统计 “本月餐饮支出”。看着 SQL 语句执行后,精准算出我(模拟的)花在咖啡上的钱,突然觉得 “数据库不再是天书,而是能帮我管理数据的助手”。

二、这门课的 “实用价值”

这门课解决的是鸿蒙应用的 “留存感” 问题 —— 用户用你的 APP,不光是当下能操作,下次打开还能接着用,甚至能记住用户习惯。“用户首选项” 让应用有 “小记性”,记住用户偏好;“关系型数据库” 让应用有 “大内存”,管理复杂业务数据;案例则教会我们 “怎么把这些数据变成用户能看到、能交互的界面”。对开发者来说,这是打造 “长期可用、体验连贯” 应用的必经之路。

三、学习感受

聊到主观感受,可得好好说说。以前我做应用,最担心 “用户数据存不住”—— 比如用户辛苦设置的主题,一重启 APP 就没了,特别影响体验。学这门课的时候,我正一边啃着便利店的饭团(赶工赶得没时间正经吃饭),一边跟着做 “用户首选项保存主题” 的 Demo。

第一次做的时候,忘了调用flush(),结果 APP 重启后,主题又回到默认了。当时有点懊恼,但仔细看课程里的 “注意事项”,才发现这个关键步骤。改完再试,APP 真的 “记住” 了我选的深色主题,那一刻的成就感,比饭团里吃到脆骨还开心。

在之前学习 “关系型数据库” 时,因为涉及 SQL 语句,我一开始有点发怵(之前对数据库操作不算熟练)。但课程把 “建表→插入→查询” 拆得特别细,还配了很多 “错误示例→正确解法” 的对比,比如 “忘记给字段加类型导致插入失败”“查询条件写错导致查不到数据”,跟着改几遍,就慢慢找到感觉了。现在我甚至能自己写简单的联表查询,那种 “掌控数据” 的感觉,让我对开发复杂应用更有底气了。

学完这门课会发现这门课程不是在教 “死技术”,而是在教 “怎么让应用更懂用户”。掌握了 “保存应用数据” 的能力,应用就不再是 “一次性工具”,而是能和用户 “长期互动” 的伙伴。就像现在,我喝完手边的美式,又能带着期待继续探索剩下的内容啦~

如果你也和我一样,在学习中经历过 “认知破碎 — 重建 — 兴奋” 的循环,或是对鸿蒙开发有无数疑问

欢迎加入 鸿蒙知识共建交流群 :点击直达

在这里,我们可以拆解技术难点,分享实践案例,甚至一起畅想全场景生态的下一个突破点 —— 毕竟,在鸿蒙的浪潮里,独行太孤独,并肩才能走得更远。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐