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

HarmonyOS应用开发之界面列表不刷新问题Bug排查记:从现象到解决完整记录

Bug排查在软件开发过程中扮演着至关重要的角色,本文采用日记形式记录了Bug排查的全过程,通过这种方式可以更加真实、详细地记录问题,便于后续追溯和经验沉淀。

Bug背景

在使用HarmonyOS的ArkUI框架开发一个卡片管理应用时,遇到了List列表界面不刷新的问题。具体表现为,虽然数据已经更新,但界面并未同步刷新显示。比如在子页面中明明添加了卡片并更新了数据,但是页面返回后,看到的卡片数量并未发生变化。

在这里插入图片描述

环境

  • 设备:HarmonyOS模拟器
  • 系统版本:HarmonyOS 5.0
  • 应用框架:ArkUI

复现条件

在多个界面的状态共享时,使用了@Provider@Consumer装饰器,同时对类和成员变量使用了@ObservedV2@Trace装饰,从而实现了数据的共享和追踪。在添加、删除或更新卡片数据时,数据操作成功,但List列表并未刷新显示最新的数据。

在这里插入图片描述

这一行就是问题所在。之前的 uid 一直是同一个。。。,结果导致数据变化,UI不会触发刷新。
之前这里的写法如下:

 ForEach(this.cardViewModel?.cardDeckList ?? [], (item: CardDeckData,index) => {Row() {CardDeck({})}}, (item: CardDeckData) => item.uid)

问题出在这个ForEach循环的最后一个参数item.uid上。

坑一:ForEach循环的key生成器

在使用ForEach循环渲染列表时,注意到最后一个参数——key生成器。这个参数用于生成列表项的唯一标识,如果设置不当,就可能导致列表项无法正确更新。

问题

数据已经更新,但通过ForEach循环渲染的列表界面并未刷新显示最新的数据。

分析与假设

分析关键代码片段,发现ForEach循环的key生成器仅使用了item.id,这样虽然可以保证唯一性,但当数据发生变化时,key并未随之更新,导致界面无法刷新。

解决方案

通过修改key生成器的逻辑,将item.id与更新的成员变量的值进行组合,确保当数据变化时,key也会随之变化,从而实现界面的正确刷新。

在这里插入图片描述

修复代码具体改动
ForEach(this.cardViewModel?.cardDeckList ?? [], (item: CardDeckData,index) => {}, (item: CardDeckData) => item.uid + '_' + JSON.stringify(item))

要保证数据更新后,不但这个key是唯一的,且是变化了的。

测试验证

修改代码后,进行了单元测试和集成测试,确认问题已解决。

坑二:列表数组中元素值改变界面不刷新

再次遇到数据不刷新的问题,通过调试发现需要对cardDeckList进行重新赋值才能刷新界面。
在这里插入图片描述

//  viewmodol/cardViewModol.ets
import DBDeckApi from "../common/api/deckApi";
import { TBCardDeckEntity } from "../model/TBCardDeck";
import { getCurrentDateTime } from "../utils/dateUtil";
import { Log } from "../utils/logutil";
import { ToastUtil } from "../utils/ToastUtil";@ObservedV2
export class CardDeckData{@Trace uid: number | undefined = 0@Trace name: string| undefined='';@Trace desc: string| undefined = ''// 卡片张数@Trace cardNum: number | undefined = 0// 已学习张数@Trace readNum: number | undefined = 0// 学习时长@Trace timeLen: number | undefined = 0// 创建时间@Trace createTime: string | undefined = ''// 更新时间@Trace updateTime: string | undefined = ''// 开始学习时间@Trace startTime: string | undefined = ''// 结束学习时间@Trace endTime: string | undefined = ''
}@ObservedV2
export class CardViewModel {private static instance: CardViewModel | null = null;@Trace cardDeckList: CardDeckData[] = []static getInstance(): CardViewModel {if (!CardViewModel.instance) {CardViewModel.instance = new CardViewModel();}return CardViewModel.instance;}/*** 添加卡组* @param timelineId 时光轴ID*/addCardDeck(name:string, desc:string) {const rec = new TBCardDeckEntity()rec.name = namerec.desc = descrec.createTime = getCurrentDateTime()DBDeckApi.insertCardDeck(rec).then((number) => {if (number < 0) {Log.error("insertCardDeck error,code=%{public}d", number)ToastUtil.showError("添加失败")} else {Log.debug("insertCardDeck ok,code=%{public}d", number)this.reloadDeckDbData()ToastUtil.showSuccess("添加成功")}})}/*** 从数据库从新加载卡组数据列表*/reloadDeckDbData(){//从数据库获取数据DBDeckApi.queryAllCardDeck().then((result) => {Log.debug(result?.length)this.cardDeckList = result ?? [];const newList = [...this.cardDeckList];this.cardDeckList = newList;// 生成新数组,确保元素为全新实例//const newList = result?.map(item => ({ ...item })) ?? [];/*this.cardDeckList = result?.map(item => ({uid: item.uid ?? 0,name: item.name ?? '',desc: item.desc ?? '',cardNum: item.cardNum,readNum: item.readNum,createTime: item.createTime,updateTime: item.updateTime,startTime: item.startTime,endTime: item.endTime} as CardDeckData)) ?? [];* *//*this.cardDeckList = result?.map(entity => {const data = new CardDeckData();// 显式属性映射(示例)data.uid = entity.uid ?? undefined;data.name = entity.name ?? ''; // 处理 undefined 转空字符串data.desc = entity.desc ?? '';data.cardNum = entity.cardNum ?? 0;data.readNum = entity.readNum ?? 0;data.createTime = entity.createTime!;data.updateTime = entity.updateTime!;return data;})?? [];* */})}

问题

在添加、删除或更新卡片数据后,虽然数据已经变化,但List列表并未刷新显示最新的数据。
必须:非得这么红框里再次赋值才可以刷新啊。显得很低效,但也很无奈。说好的@ObservedV2和@Trace装饰类和成员变量,怎么没观察到成员变量的变化呢?

分析与假设

reloadDeckDbData方法中,直接将数据库查询到的数据赋值给cardDeckList,但由于赋值的对象引用没有变化,导致界面未能检测到数据变化。

解决方案

通过重新生成一个新的数组,并将查询到的数据赋值给新的数组,然后再将新的数组赋值给cardDeckList,这样可以确保对象引用发生变化,从而实现界面的正确刷新。

修复代码具体改动
DBDeckApi.queryAllCardDeck().then((result) => {Log.debug(result?.length)this.cardDeckList = result ?? [];const newList = [...this.cardDeckList];this.cardDeckList = newList;
})

测试验证

修改代码后,进行了单元测试和集成测试,确认问题已解决。

影响范围

  • 功能模块:卡片管理模块
  • 用户体验:用户无法看到最新的卡片数量,影响操作感受
  • 业务逻辑:无法准确显示业务数据,影响应用功能

经验总结

技术收获

  • 使用@ObservedV2@Trace装饰类和成员变量,可以方便地追踪和处理数据变化。
  • ForEach循环的key生成器必须能够正确地反映数据变化,从而保证界面的正确刷新。
  • 重新赋值对象以改变其引用,可以确保界面能够正确检测到数据变化并进行刷新。

流程优化

  • 使用代码审查(Code Review)和测试策略可以避免类似的问题。
  • 在开发过程中,应充分考虑框架特性和代码逻辑,避免不必要的“坑”。
  • 通过调试器和日志分析工具等工具,可以快速定位问题并进行修复。

附录

相关技术文档链接

  • ArkUI ForEach文档
  • HarmonyOS数据共享文档

参考工具清单

  • HarmonyOS调试器
  • Log分析工具(如Logcat)
  • Sentry(错误监控系统)

通过以上记录,希望能够帮助其他开发者避免在使用ArkUI时遇到类似的问题,同时也为提升自身的代码质量和开发效率提供了一定的参考。


文章转载自:

http://JbFAURZ8.cLzLy.cn
http://gvd6d9rX.cLzLy.cn
http://y9ZA7Egi.cLzLy.cn
http://zmk9TuOJ.cLzLy.cn
http://xIJX6ofc.cLzLy.cn
http://E5T9EmzE.cLzLy.cn
http://GnKgStOK.cLzLy.cn
http://9KFO6X8L.cLzLy.cn
http://hn3ojM4U.cLzLy.cn
http://ZPQhSIks.cLzLy.cn
http://X207UMbx.cLzLy.cn
http://lFRJ8MjY.cLzLy.cn
http://WBi9lgMM.cLzLy.cn
http://pVlCRm3K.cLzLy.cn
http://hXbddTPF.cLzLy.cn
http://6W4mGK2t.cLzLy.cn
http://ps9NWuJV.cLzLy.cn
http://npO06LcX.cLzLy.cn
http://EZzNCj3L.cLzLy.cn
http://IZh6xJAe.cLzLy.cn
http://BRI1jBNE.cLzLy.cn
http://Ol7NhhE4.cLzLy.cn
http://0fjthlKG.cLzLy.cn
http://Q2ayFede.cLzLy.cn
http://RcgJSmSm.cLzLy.cn
http://fjK8y6EV.cLzLy.cn
http://PncFFpGn.cLzLy.cn
http://0osfnOsU.cLzLy.cn
http://xmzs1Sq0.cLzLy.cn
http://7ncfNhvA.cLzLy.cn
http://www.dtcms.com/a/366912.html

相关文章:

  • 如何架设游戏服务器
  • 如何配置安全的 SFTP 服务器?
  • 【连载 1/9】大模型基础入门学习60页大模型应用:(一)绪论【附全文阅读】
  • Vue基础知识-脚手架开发-初始化目录解析
  • Java面试-HashMap原理
  • 开关电源——只需这三个阶段,从电源小白到维修大神
  • Pydantic模型验证测试:你的API数据真的安全吗?
  • Linux高手才知道的C++高性能I/O秘诀:Vector I/O与DMA深度解析
  • DRMOS电源
  • 经典资金安全案例分享:支付系统开发的血泪教训
  • 手机秒变全栈IDE:Claude Code UI的深度体验
  • Go 自建库的使用教程与测试
  • 生活在数字世界:一份人人都能看懂的网络安全生存指南
  • 【gemini】2.5 Flash费用估算
  • DirectX修复必备指南:解决DLL缺失与游戏崩溃的5种方法
  • 如何建立一套切实可行的绩效考核体系:参考这三个前提、五大步骤、三个案例
  • 火山引擎数据智能体DataAgent总结分享
  • 基于51单片机智能大棚浇花花盆浇水灌溉补光散热设计
  • 深度学习-----修改学习率来优化模型的几个方法
  • 第4章 用户界面与基本操作
  • 从课前到课后,地理创新实验室赋能教学新范式
  • 焊接自动化测试平台图像处理分析-模型训练推理
  • Kafka面试精讲 Day 7:消息序列化与压缩策略
  • 【图像处理基石】如何在频域对图像进行处理和增强?
  • 探索 PostgreSQL 和 MySQL 之间的主要差异和相似之处,找到满足您项目需求的最佳数据库解决方案。
  • Python爬虫常用框架
  • HTTP与HTTPS杂谈-HTTPS防御了什么
  • Caffeine 双端队列优化揭秘:如何优雅维护访问和写入顺序
  • 02-ideal2025 Ultimate版安装教程
  • 代码随想录刷题Day49