Android DiffUtil未正确触发notify致RecyclerView全量刷新问题
问题根源
问题出在DiffUtil.ItemCallback的条目匹配逻辑和Room的数据返回特性不匹配:
- 你当前
areItemsTheSame方法使用===做引用相等判断,只有两个对象指向同一内存地址时才会被识别为同一条目 - Room在数据变更后返回的
LiveData会发射全新构造的列表,列表内的所有实体类对象都是新实例,和旧列表中的对象不存在内存地址复用 - 这种场景下所有新旧条目的
areItemsTheSame判断都会返回false,DiffUtil会判定旧列表全量删除、新列表全量新增,最终触发整个RecyclerView重载,不会执行局部插入/删除的刷新逻辑。
另外需要确认:你的Mark实体必须是Kotlin data class(或正确重写了equals方法),否则areContentsTheSame中的==判断也会失效。
修复步骤
- 修改
MarksComparator的areItemsTheSame实现,使用实体的唯一主键(即你在Mark类中用@PrimaryKey标注的字段,通常是id)判断是否为同一条目,禁止使用引用相等判断:
class MarksComparator : DiffUtil.ItemCallback<Mark>() { override fun areItemsTheSame(oldItem: Mark, newItem: Mark): Boolean { // 替换成你自己Mark实体的主键字段名 return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: Mark, newItem: Mark): Boolean { return oldItem == newItem } }
- 校验
Mark实体定义,确保是data class,主键字段不可变:
@Entity data class Mark( @PrimaryKey(autoGenerate = true) val id: Int = 0, // 替换为你实际的业务字段 val title: String, val createTime: Long )
- 提交列表时不要额外做修改或拷贝,直接将Room返回的列表传入
submitList即可,ListAdapter内部会自动持有旧列表引用做差异计算。
修改完成后,新增/删除条目时DiffUtil会正确计算差异,调用对应notifyItem*方法执行局部刷新,不会再触发全列表重载。
内容的提问来源于stack exchange,提问作者user19107982
相关产品推荐
相关产品推荐

