RecyclerView 源码分析(六)DiffUtil的差量算法分析

转载自琼珶和予

DiffUtil的差量算法分析

首先,估计有一部分的同学可能还不知道DiffUtil是什么,说实话,之前我也根本不了解这是什么东西。DiffUtil是我在公司实习的时候了解到的一个类,在那之前,我使用RecyclerView的方式也是大部分的人差不多,就是RecyclerView和它的四大组成部分任意组合。

当时在公司第一次看到这个东西的时候,立即两眼发光,非常好奇这是什么东西,就好像在大街上看到美女一样。后来在非工作时间的时候,我去了解了一下这个类,不过当时也只是简单的了解这个东西。现在在系统的学习RecyclerView的源码,我觉得有必要深入的了解和学习一下这个东西–DiffUtil。

本文参考资料:
Investigating Myers’ diff algorithm: Part 1 of 2

本文有一部分的内容来自上文的翻译。我的建议是,各位同学可以直接看上面的文章,大佬的文章已经将DiffUtil的核心算法讲的非常透彻。

本文打算从三个角度来分析DiffUtil

  1. DiffUtil的基本使用
  2. Myers差量算法的深入探究
  3. DiffUtil的Myers算法实现以及DiffUtil怎么跟Adapter联系起来的

1. 概述

在正式分析DiffUtil之前,我们先来对DiffUtil有一个大概的了解–DiffUtil到底是什么东西。

我们相信大家都遇到一种情况,那就是在一次操作里面可能会同时出现remove、add、change三种操作。像这种情况,我们不能调用notifyItemRemoved、notifyItemInserted或者notifyItemChanged方法,为了视图立即刷新,我们只能通过调用notifyDataSetChanged方法来实现。

而notifyDataSetChanged方法有什么缺点呢?没有动画!对,通过调用notifyDataSetChanged方法来刷新视图,每个操作是没有动画,这就很难受了!

有没有一种方式可以实现既能保留动画,又能刷新动画呢?我们单从解决问题的角度来说,我们可以设计一种算法,来比较变化前后的数据源有哪些变化,这里的变化包括,如上的三种操作。哪些位置进行了change操作,哪些地方进行了add操作,哪些地方进行了remove操作,可以通过这种算法计算出来。

Google爸爸考虑到这个问题大家都能遇到,那我帮你们实现,这样你们就不用自己去实现了,这就是DiffUtil的由来。

2. DiffUtil的基本使用

在正式分析DiffUtil的源码之前,我们先来看看DiffUtil的基本使用,然后我们从基本使用入手,这样看代码的时候才不会迷茫。

我们想要使用DiffUtil时,有一个抽象类Callback是我们必须了解的,我们来看看,了解它的每个方法都都有什么作用。

方法名 作用
getOldListSize 原数据源的大小
getNewListSize 新数据源的大小
areItemsTheSame 判断给定两个Item的是否同一个Item。给定的是两个Position,分别是原数据源的位置和新数据源的位置。判断两个Item是否是同一个Item,如果是不同的对象(新数据源和旧数据源持有的不是同一批对象,新数据源可能是从旧数据源那里深拷贝过来,也有重新进行网络请求返回的),可以给每个Item设置一个id,如果是同一个对象,可以直接使用==来判断
areContentsTheSame 判断给定的两个Item内容是否相同只有areItemsTheSame返回为true,才会回调此方法。也就是说,只能当两个Item是同一个Item,才会调用此方法来判断给定的两个Item内容是否相同
getChangePayload 用于局部刷新,回调此方法表示所给定的位置肯定进行change操作,所以这里不需要判断是否为change操作

简单的了解Callback每个方法的作用之后,我们现在来看看DiffUtil是怎么使用的。

我们先来看看ItemCallback是怎么实现的:

public class RecyclerItemCallback extends DiffUtil.Callback {
   

    private List<Bean> mOldDataList;
    private List<Bean> mNewDataList;

    public RecyclerItemCallback(List<Bean> oldDataList, List<Bean> newDataList) {
   
        this.mOldDataList = oldDataList;
        this.mNewDataList = newDataList;
    }

    @Override
    public int getOldListSize() {
   
        return mOldDataList.size();
    }

    @Override
    public int getNewListSize() {
   
        return mNewDataList.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
   
        return Objects.equals(mNewDataList.get(newItemPosition).getId(), mOldDataList.get(oldItemPosition).getId());
    }

    @Override
    public boolean areContentsTheSame(int i, int i1) {
   
        return Objects.equals(mOldDataList.get(i).getContent(), mNewDataList.get(i1).getContent());
    }
}

这里,areItemsTheSame方法是通过id来判断两个Item是不是同一个Item,其次areContentsTheSame方法是通过判断content来判断两个Item的内容是否相同

然后,我们再来看看DiffUtil是怎么使用的:

private void refreshData() {
   
    final List<Bean> oldDataList = new ArrayList<>();
    final List<Bean> newDataList = mDataList;

    // deep copy
    for (int i = 0; i < mDataList.size(); i++) {
   
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值