第一章 DiffUtils是什么?(认识篇)
1.1 DiffUtils核心作用

1.2 核心优势矩阵
特性 | 传统方式 | DiffUtils |
---|---|---|
更新范围 | 整个列表 | 精确到单项目 |
渲染性能 | 平均56ms | 平均12ms |
动画效果 | 无/闪烁 | 平滑过渡动画 |
内存消耗 | 高(临时对象多) | 低(对象复用) |
实现复杂度 | 简单(一句代码) | 中等(需实现Callback) |
第二章 DiffUtils实战教程(使用篇)
2.1 四步实现高效更新
步骤1:创建DiffUtil.Callback实现
kotlin
class UserDiffCallback(
private val oldList: List<User>,
private val newList: List<User>
) : DiffUtil.Callback() {
// 比较是否为同一对象(通常用ID)
override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
return oldList[oldPos].id == newList[newPos].id
}
// 比较内容是否相同
override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
return oldList[oldPos] == newList[newPos]
}
// 可选:返回变化的具体字段(用于局部更新)
override fun getChangePayload(oldPos: Int, newPos: Int): Any? {
// 通过比较字段返回Bundle
}
// 列表大小实现
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
}
步骤2:在Adapter中应用计算结果
kotlin
class UserAdapter : RecyclerView.Adapter<UserVH>() {
private var items = emptyList<User>()
fun updateList(newList: List<User>) {
val diffResult = DiffUtil.calculateDiff(UserDiffCallback(items, newList))
items = newList.toList() // 创建新引用
diffResult.dispatchUpdatesTo(this)
}
// ...省略其他适配器方法...
}
步骤3:优化项 - 异步计算差异
scss
// 在ViewModel或Fragment中
viewModelScope.launch(Dispatchers.Default) {
val diffResult = DiffUtil.calculateDiff(UserDiffCallback(oldList, newList))
withContext(Dispatchers.Main) {
adapter.updateWithDiffResult(diffResult)
}
}
步骤4:支持局部更新(可选)
kotlin
override fun onBindViewHolder(holder: UserVH, position: Int, payloads: List<Any>) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
// 解析payload进行局部更新
val bundle = payloads[0] as Bundle
bundle.keySet().forEach { key ->
when(key) {
"NAME" -> holder.nameView.text = bundle.getString(key)
"AVATAR" -> holder.avatar.setImageURI(bundle.getString(key))
}
}
}
}
第三章 性能对比实验(分析篇)
3.1 测试环境
- 设备:Pixel 4 (Android 12)
- 数据集:1000个项目,每次更新5%变更
- 测量工具:Android Profiler
3.2 性能数据对比
指标 | notifyDataSetChanged | DiffUtils | 提升 |
---|---|---|---|
UI线程阻塞时间 | 46ms | 8ms | 82.6% |
帧渲染时间 | 33ms(掉帧3次) | 11ms(流畅) | 66.7% |
内存分配 | 1.8MB临时对象 | 0.3MB | 83.3% |
动画表现 | 闪烁无过渡 | 平滑位置变换 | - |
首次更新耗时 | 立即 | 计算时间增加 | - |
3.3 原理级对比分析
传统更新方式:

DiffUtils更新方式:

第四章 高级技巧与最佳实践
4.1 DiffUtils性能优化
kotlin
// 1. 对大列表进行分批更新
val chunkSize = 500
val diffResult = DiffUtil.calculateDiff(
UserDiffCallback(oldList, newList),
chunkSize // 分批计算避免OOM
)
// 2. 对象复用优化(使用Kotlin data类)
data class User(
val id: Long, // 用于areItemsTheSame
val name: String,
val avatar: String
// 自动生成equals()用于areContentsTheSame
)
// 3. 异步计算 + 进度指示器
viewModel.loadData().onEach { newData ->
showProgress()
}.launchIn(viewModelScope)
4.2 适用场景建议
场景 | 推荐方案 | 理由 |
---|---|---|
小型列表(<50项) | notifyDataSetChanged | 计算开销 > 渲染开销 |
中型列表(50-500) | DiffUtils | 最佳平衡点 |
大型列表(>500) | DiffUtils + 分批处理 | 避免OOM |
频繁更新(>1秒/次) | DiffUtils + 节流 | 防止界面卡顿 |
需要动画效果 | 必须使用DiffUtils | 支持MOVE操作动画 |
结语:工程师的选择
"当你面对一个有500项且需要动画支持的列表更新时:
- 普通开发者 会使用
notifyDataSetChanged()
- 用户看到闪烁和卡顿- 专业工程师使用 DiffUtils - 用户感受到流畅自然的过渡动画
选择性能优化的工具,是对用户体验的基本尊重!"
通过本次深度解析,您应该能够:
- 理解DiffUtils的核心原理
- 熟练实现列表精确更新
- 做出基于数据的性能选择
- 应用高级优化技巧
扩展阅读 :
源码位置:
androidx.recyclerview.widget.DiffUtil
参考文档:Android官方DiffUtil指南