当前位置: 首页 > 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://www.xdnf.cn/news/1459495.html

相关文章:

  • JS函数进阶
  • Roo Code之自定义指令(Custom Instructions),规则(Rules)
  • 硬盘分区格式化后产生了哪些变化
  • OpenStack VLAN网络类型实训案例
  • 机器学习:后篇
  • LangChain4j的初步学习【逐步添加中】
  • 强化学习DQN解决Cart_Pole问题
  • claude code route 使用教程|命令大全
  • linux中的awk使用详解
  • 深度解读《实施“人工智能+”行动的意见》:一场由场景、数据与价值链共同定义的产业升级
  • 【8】C#上位机---泛型、委托delegate与多线程Task
  • 2025年代理IP服务深度评测:三大平台横评,谁是最强业务助手?
  • 检查数据集格式(77)
  • 计算机二级C语言操作题(填空、修改、设计题)——真题库(16)附解析答案
  • C++基础——模板进阶
  • 【C++题解】关联容器
  • Linux的权限详解
  • 一次死锁的排查
  • 激活函数:神经网络的“灵魂开关”
  • 阅读论文神奇Zotero下载安装教程以及划词翻译(Translate for Zotero)的配置
  • 动态内存管理柔性数组
  • Vue 中绑定样式的几种方式
  • Process Explorer 学习笔记(第三章3.1.1):度量 CPU 的使用情况详解
  • 【Unity知识分享】Unity接入dll调用Window系统接口
  • 无限时长视频生成新突破!复旦联合微软、腾讯混元推出StableAvatar,仅需1张照片+1段音频实现真人说话视频
  • hutool的EnumUtil工具类实践【持续更新】
  • 揭秘23种设计模式的艺术与技巧之行为型
  • 美联储计划召开稳定币和代币化创新会议
  • 大数据框架Doris全面解析
  • 期权平仓后权利金去哪了?