当前位置: 首页 > news >正文

HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(二十八):【数据持久化】收藏与浏览历史——让数据在 App 重启后依然“活着”

HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(二十八):【数据持久化】收藏与浏览历史——让数据在 App 重启后依然“活着”

摘要:收藏一道菜谱、回顾之前看过什么菜——这些功能在前 27 篇中只能活在内存里。App 重启后,所有收藏和历史全部消失。本篇利用已有的RelationalStoreHelper(完整 CRUD 封装),新增三张持久化表,让收藏和历史在 App 重启后依然存在。你还会学到:为什么收藏用主键约束而历史用追加写入?为什么浏览历史的写入失败不阻塞页面跳转?以及,如何用约 45 行代码完成从建表到 UI 联动的完整持久化闭环。


一、引言:内存的“失忆症”

一个有趣的测试:在第 27 篇的基础上,收藏一道“番茄牛腩煲”,然后关掉 App,重新打开。

收藏按钮恢复成了空心——App 完全忘记了刚才的操作。

这不是 Bug,这是内存的失忆症。前 27 篇中,所有用户数据——收藏的菜谱、浏览过的记录、个人偏好——都存储在@State@Local变量中。这些变量的生命周期与组件绑定,组件销毁时数据也随之消失。

数据类型存储位置生命周期App 重启后
推荐结果HomeViewModel.recommendedRecipes页面级❌ 消失
收藏状态RecipeDetailPage.heartLiked组件级❌ 消失
浏览记录无存储❌ 从未存在过

🎯本篇目标:利用已有的RelationalStoreHelper,新增收藏表、浏览历史表和社区分享表,配合 UI 层的两处微小改动,让数据在 App 重启后依然“活着”。核心代码仅约 45 行。


二、核心原理:关系型数据库的“记账本”模型

2.1 为什么是 SQLite 而非 Preferences?

HarmonyOS 提供了两种本地持久化方案:

方案数据结构查询能力适用场景
Preferences键值对仅 get(key)简单配置(头像路径、昵称)
RelationalStore(SQLite)表 + SQLSELECT/INSERT/DELETE/ORDER BY结构化数据(收藏、历史、订单)

收藏和历史属于后者——你需要按时间排序查询“最近浏览的 10 道菜”,需要判断“这道菜是否已收藏”。这些需求用键值对也能实现(把所有数据序列化为 JSON 存一个 key),但查询效率低、代码丑陋、容易出错。

关系型数据库就像一个记账本:每笔收藏是一行,每笔浏览也是一行。你可以随时翻阅(SELECT)、追加(INSERT)、划掉(DELETE),不需要关心这本账怎么保存——SQLite 替你管。

2.2 已有的基础设施

前 27 篇中,我们已经在RelationalStoreHelper中建立了完整的 CRUD 封装——initDatabase()executeSql()insert()方法。这些方法已用于用户登录注册的本地存储(local_users表)。本篇不新增任何数据库基础设施,只扩展现有实例。


三、表结构设计:每一列都有存在的理由

在已有的数据库LingxiKitchen.db中新增三张表:

-- 收藏表(recipe_id 为主键,保证同一菜谱只收藏一次)CREATETABLEIFNOTEXISTSfavorite_recipes(recipe_idINTEGERPRIMARYKEY,recipe_nameTEXTNOTNULL,saved_atINTEGERDEFAULT(strftime('%s','now')));-- 浏览历史表(每次浏览追加一条,不设主键约束)CREATETABLEIFNOTEXISTSrecipe_history(recipe_idINTEGERNOTNULL,recipe_nameTEXTNOTNULL,viewed_atINTEGERDEFAULT(strftime('%s','now')));-- 社区分享表(预留,为社区功能做准备)CREATETABLEIFNOTEXISTSshared_recipes(idINTEGERPRIMARYKEYAUTOINCREMENT,user_nameTEXTNOTNULL,recipe_nameTEXTNOTNULL,ingredientsTEXT,stepsTEXT,shared_atINTEGERDEFAULT(strftime('%s','now')));

建表语句在RelationalStoreHelper.createTables()中追加,使用CREATE TABLE IF NOT EXISTS保证幂等——重复调用不会出错。

设计考量

设计点选择理由
favorite_recipes主键recipe_id收藏是唯一性操作——同一菜谱只需一条记录,取消收藏时 DELETE,重新收藏时 INSERT
recipe_history主键无(追加写入)浏览是可重复操作——用户可能三天看五次“番茄牛腩煲”,完整时间线比最新记录更有分析价值
saved_at/viewed_at默认值strftime('%s','now')SQLite 内置时间函数,无需在 ArkTS 侧传入时间戳,减少代码量和时钟偏差风险
shared_recipes步骤字段TEXT(JSON 序列化)步骤是数组结构,SQLite 不直接支持数组类型,JSON 序列化是最简单的跨语言兼容方案

四、收藏逻辑:INSERT 与 DELETE 的一体两面

4.1 写入时机

RecipeDetailPage底部操作栏的收藏按钮中,在onClick中调用this.toggleFavorite()

.onClick(()=>{this.heartLiked=!this.heartLiked;this.heartScale=1.3;setTimeout(()=>{this.heartScale=1;},150);this.toggleFavorite();// ← 新增持久化,在动画播放的同时异步写入})

4.2 toggleFavorite 方法

privateasynctoggleFavorite():Promise<void>{try{if(this.heartLiked){awaitstoreHelper.insert('favorite_recipes',{recipe_id:this.recipe.id,recipe_name:this.recipe.name,saved_at:Date.now()});}else{awaitstoreHelper.executeSql('DELETE FROM favorite_recipes WHERE recipe_id = ?',[this.recipe.id.toString()]);}}catch(err){console.error('[RecipeDetail] 收藏持久化失败:',JSON.stringify(err));}}
SQLiteRelationalStoreHelper收藏按钮👤 用户SQLiteRelationalStoreHelper收藏按钮👤 用户点击收藏heartLiked = truescale 弹跳动画insert('favorite_recipes', {id, name})INSERT INTO favorite_recipesrowId再次点击(取消)executeSql('DELETE WHERE id=?')DELETE FROM favorite_recipesok

图一解读:收藏和取消收藏是同一操作的两个方向——用recipe_id作为主键,INSERT 和 DELETE 对称操作。数据库不关心用户是第一次收藏还是取消后重新收藏——它只执行 SQL,由 ArkTS 侧的heartLiked状态决定方向。

4.3 设计考量:为什么不阻塞动画?

toggleFavorite()是异步的,但onClick没有await它。这意味着动画先播(150ms 弹跳),数据库写入在后台并行进行。如果数据库写入失败(比如磁盘满),用户已经看到了动画反馈——这会不会不一致?

不会。收藏功能的核心价值是再次打开 App 时还能看到收藏,而不是“点击瞬间的数据一致性”。如果写入失败,下次打开 App 时收藏会丢失——这确实是个问题,但它发生的概率远低于用户因为等待 I/O 而感知到的卡顿。用户体验的优先级是:即时反馈 > 数据持久化 > 错误处理。前两者保证了“好用”,第三者保证了“不出大问题”。


五、浏览历史:追加写入,静默失败

5.1 写入时机

Index.etshandleRecipeTap方法开头新增写入:

privatehandleRecipeTap(recipe:Recipe):void{// ★ 写入浏览历史(失败不阻塞跳转)try{storeHelper.insert('recipe_history',{recipe_id:recipe.id,recipe_name:recipe.name,viewed_at:Date.now()});}catch(_err){}// 原有跳转逻辑(不受历史写入影响)this.getUIContext().getRouter().pushUrl({...});}

5.2 设计考量:为什么静默吞错误?

浏览历史不是关键路径。用户点击菜谱卡片时的核心诉求是看到菜谱详情,而不是“确保这次浏览被记录”。如果数据库写入失败(磁盘满、表损坏),阻塞跳转或弹出错误提示都会严重破坏体验。

失败

用户点击菜谱卡片

写入浏览历史

跳转详情页

静默忽略

用户看到菜谱详情 ✅

图二解读:浏览历史是一条分叉路——主路径(跳转)和副路径(写入)并行。副路径失败不影响主路径。这是“非关键路径静默失败”的设计模式——适用于所有“有更好、没有也行”的增值功能。

5.3 为什么是追加而非更新?

如果用户三天内看了五次“番茄牛腩煲”,你应该存五条记录还是一条记录?

策略存储方式能回答的问题
更新(UPDATE)一条记录,更新viewed_at“最近什么时候看过这道菜”
追加(INSERT)五条记录,各自有时间戳+ “看过多少次” + “什么时候最常看” + “看了之后收藏了吗”

追加的成本只是多占几行磁盘空间(每条约 100 字节),但换来了完整的行为时间线。后续可以扩展“最近浏览”列表、“猜你喜欢”推荐、“看了但没收藏”提醒等功能。追加不是冗余,是未来数据分析的基础设施。


六、代码交付清单

文件新增/修改行数说明
RelationalStoreHelper.ets修改+25createTables新增三张建表 SQL
RecipeDetailPage.ets修改+15收藏按钮加入toggleFavorite()调用
Index.ets修改+5handleRecipeTap新增浏览历史写入

七、设计决策

决策选择理由
收藏表主键recipe_id收藏是唯一性操作,同一菜谱只需一条记录
历史表写入策略追加(INSERT)而非更新保留完整行为时间线,为数据分析打基础
历史写入失败处理静默吞错误,不阻塞跳转浏览历史是增值功能,非关键路径
动画与持久化的顺序动画先播,持久化异步并行用户感知的延迟来自 I/O,动画填补了这段空白
不新建 DataSource 类直接复用storeHelper单例已有完整 CRUD,不引入额外抽象层

八、本阶段总结与下篇预告

本篇用约 45 行新增代码,让《灵犀厨房》的收藏和浏览历史从“内存失忆”变为“持久记忆”:

  • 三张新表favorite_recipes(收藏)、recipe_history(浏览历史)、shared_recipes(预留社区)
  • 收藏的 INSERT/DELETE 对称操作recipe_id主键让收藏和取消是同一操作的镜像
  • 浏览历史的追加写入:静默失败不阻塞跳转,完整时间线为未来数据分析打基础
  • 最小侵入:UI 层仅两处改动,数据库基础设施复用已有封装

现在重新打开 App,收藏依然在,浏览历史可追溯——App 开始有了“记忆”。

下篇预告:第 29 篇《个人中心:偏好持久化与推荐联动》。我们将把用户的口味偏好、忌口设置和健康档案持久化到本地数据库,并让推荐引擎在下次启动时自动读取这些偏好——真正做到“越用越懂你”。


📚 本系列持续更新中:下一篇将让推荐引擎与用户偏好联动,开启个性化推荐的正循环。

🔗专栏入口:[《HarmonyOS6.1全场景实战》合集]

📦 获取基线版本源码包:包括第1-15篇所有代码 + 架构文档 + Flask 后端

如果你觉得这篇文章对您有所帮助,麻烦您动动发财之手点赞 👍、收藏 ⭐ 和评论 💬。谢谢大家!!

http://www.gsyq.cn/news/1469810.html

相关文章:

  • 如何轻松上手Ragas:LLM应用评估的终极指南
  • 极域电子教室破解技术深度解析:从内核驱动到用户态对抗的完整方案
  • Shiply 2026 自研升级API方案对比
  • 2026南宁家政公司十大排名,口碑第一名花落谁家?看完这篇不纠结 - 教育信息速递
  • 【AI工具学习路径规划避坑白皮书】:基于237个真实学习案例的路径失效根因分析(附可执行诊断清单)
  • 六家 GEO 系统服务商实测横向测评,按企业发展周期筛选 TOP 推荐厂商
  • 农业AI入门:5分钟看懂植物叶片‘健康指纹’——高光谱反射曲线
  • 为什么2026年将成为AI Agent元年
  • 2026 天津专升本机构五星排名榜单|本土老牌艺大教育综合实力稳居第一梯队
  • 标书打印:矮萝卜如何守护企业的关键时刻
  • 从手机信号到无人机图传:揭秘‘自由空间公式’如何影响你身边所有无线设备的‘命脉’——距离
  • 计算机毕业设计之django餐饮店营收信息管理系统的设计与实现
  • 如何免费获取Steam创意工坊模组:WorkshopDL终极指南
  • 如何免费精准计算AI提示词token成本?TikTokenizer完整指南
  • 2026 年郑州地区化妆品柜展柜行业技术与服务对标分析报告
  • 广州市大金中央空调维修师傅电话|各区金牌师傅,靠谱选欧米到家
  • 新手编程入门:在快马平台从零到一构建你的第一个电子宠物‘香香’
  • 别再硬算任务分配了!用Python手搓匈牙利算法,5分钟搞定运筹学指派问题
  • 南宁租房党/搬家党保洁攻略:押金能不能拿回来,就看这一把 - 教育信息速递
  • VS Code 1.122 重磅登场:AI 全面自主,浏览器变身专业测试仪
  • 南宁家政服务项目大全:从日常保洁到开荒收纳,一篇告诉你该选哪个 - 教育信息速递
  • 告别论文难产!好用的AI论文写作助手汇总 - 品牌测评鉴赏家
  • Sora 2双通路比特率控制器(DBRC)技术解密(含训练时bitplane masking梯度掩码矩阵原始配置)
  • 世卫大会健康中国建设成果 健康优先全球发布大健康医药产业理论体系
  • 从安卓APK到Python脚本:一次搞懂Msfvenom跨平台Payload生成的核心参数与避坑指南
  • Mac用户速查!:M2 Ultra vs M3 Max运行Phi-3-mini的Metal加速瓶颈定位(GPU共享内存带宽饱和点已锁定)
  • 2026年餐饮数智化转型实战指南
  • 南宁养宠家庭保洁指南:猫狗双全的日子,怎么才能不牺牲家里干净度? - 教育信息速递
  • HSTracker:3步打造你的炉石传说智能对战助手,让每场对战都充满洞察力
  • 让virtualbox更聪明:基于快马ai的虚拟机智能配置与调优方案生成