DiffUtil未触发AdapterDataObserver问题排查及响应式方案修正
首先,你的AdapterDataObserver没触发的直接原因很容易忽略:你只重写了onChanged()方法,但DiffUtil默认会生成局部数据更新的通知(比如notifyItemInserted、notifyItemChanged等),这些通知会触发Observer的对应方法(如onItemRangeChanged、onItemRangeInserted),而不是全局的onChanged()。只有当DiffUtil计算出整个数据集完全替换时,才会调用onChanged()。
第一步:修复Observer的回调覆盖
先修改Fragment中的Observer,覆盖所有你需要监听的方法,比如:
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onChanged() { Log.d("Observer", "整个数据集发生变化") } override fun onItemRangeChanged(positionStart: Int, itemCount: Int) { Log.d("Observer", "局部数据变化: 从位置$positionStart开始,共$itemCount条") } override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { Log.d("Observer", "插入数据: 从位置$positionStart开始,共$itemCount条") } // 根据业务需要,还可以覆盖onItemRangeRemoved、onItemRangeMoved等方法 })
这样你就能看到DiffUtil触发的回调了。
第二步:修复响应式实现的潜在问题
你的RxJava实现存在几个风险点,需要调整优化:
1. 数据源的线程安全与不可变性问题
你直接使用传入的MutableList<Message>,如果外部代码修改了这个列表,会导致DiffUtil计算差异时使用的新旧数据不一致。建议在setItems时创建新的列表副本,隔离外部修改:
private fun setItems(newItems: List<Message>) { items.clear() items.addAll(ArrayList(newItems)) // 创建副本,避免外部修改影响内部数据源 }
2. 订阅的内存泄漏风险
你的updater是PublishSubject,但没有在Adapter销毁时取消订阅,会导致内存泄漏。建议在Adapter中保存订阅的Disposable,并在onDetachedFromRecyclerView时销毁:
private var disposable: Disposable? = null init { items = ArrayList() disposable = updater .debounce(500, TimeUnit.MILLISECONDS) .subscribeOn(Schedulers.computation()) .map { newItems -> val diffResult = DiffUtil.calculateDiff(DiffUtilCallback(newItems, items), true) Pair(newItems, diffResult) } .observeOn(AndroidSchedulers.mainThread()) .subscribe { (newItems, diffResult) -> setItems(newItems) diffResult.dispatchUpdatesTo(this) } } override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { super.onDetachedFromRecyclerView(recyclerView) disposable?.dispose() }
这里我把setItems和dispatchUpdatesTo都移到了主线程的subscribe块中,确保数据源更新和UI通知都在主线程执行(之前的doOnNext是在computation线程,虽然修改List本身线程安全,但后续和UI相关的操作统一在主线程更稳妥)。
3. 确保DiffUtilCallback的正确性
你的DiffUtilCallback实现直接影响DiffUtil的差异计算结果,一定要保证这两个核心方法的正确性:
class DiffUtilCallback( private val newList: List<Message>, private val oldList: List<Message> ) : DiffUtil.Callback() { override fun getOldListSize() = oldList.size override fun getNewListSize() = newList.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 必须用item的唯一标识判断是否为同一个对象,比如Message的id字段 return oldList[oldItemPosition].id == newList[newItemPosition].id } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 判断item内容是否一致,建议重写Message的equals方法,或者逐一比较关键字段 return oldList[oldItemPosition] == newList[newItemPosition] } }
如果这两个方法实现错误,DiffUtil会计算出错误的差异,导致通知不符合预期。
总结
- 先覆盖Observer的所有相关回调方法,才能捕获DiffUtil的局部更新通知;
- 调整RxJava订阅的生命周期,避免内存泄漏;
- 确保数据源的不可变性和DiffUtilCallback的正确性,保障差异计算的准确。
内容的提问来源于stack exchange,提问作者Majkeee

