Sachit Wadhawan

How to Use DiffUtil with RecyclerView Adapter in Android?

DiffUtil With RecyclerView adapter

What is DiffUtil?

A utility class that finds the difference between two lists and provides the updated list as an output. We can use this utility class to notify updates to a RecyclerView Adapter.

When the content of our list gets changed, we have to call notifyDataSetChanged for getting the updates but it is very costly. There are so many iterations for getting the job done in the case of notifyDataSetChanged.

As of 24.2.0, RecyclerView Support Library, the v7 package offers a utility class called DiffUtil. It does its job perfectly. It is based on Eugene Myers’ algorithm.

Comparison with notifyDataSetChanged

If you know the implementation of RecyclerView you may also know that below mentioned method will be required to notify the changes to the adapter.

  • notifyItemChanged
  • notifyItemInserted
  • notifyItemRemoved

And their corresponding Range variations

  • notifyItemRangeChanged
  • notifyItemRangeInserted
  • notifyItemRangeRemoved

Their names explain their purpose pretty well. According to the official documentation, they are significantly more efficient than the famous notifyDataSetChanged(). DiffUtil uses these methods to notify the RecyclerView for any changes in the data set

Let’s take a scenario, where we are using some filtration on data. Here we will sort data according to the rating.

DataRepository.class

public class DataRepository {

    public static List<Rating> getRatingList(){
        List<Rating> ratings = new ArrayList<>();
        ratings.add(new Rating(1, 10, "Batman"));
        ratings.add(new Rating(2, 12, "Hal Jordan"));
        ratings.add(new Rating(3, 5, "Captain America"));
        ratings.add(new Rating(4, 22, "Thor"));
        return ratings;
    }

    public static List<Rating> sortByRating(List<Rating> oldList){
        Collections.sort(oldList, new Comparator<Person>() {
            @Override
            public int compare(Rating rating1, Rating rating2) {
                return rating1.rate - rating2.rate;
            }
        });
        return oldList;
    }
}

 

 

DiffUtil.Callback is an abstract class that has both abstract and non-abstract methods in it. Let’s take a look at the adapter and callback class first.

RatingAdapter.class

public class RatingAdapter extends RecyclerView.Adapter<RatingAdapter.ViewHolder> {

    private List<Rating> ratings = new ArrayList<>();

    public RatingAdapter(List<Rating> ratingList) {
        this.ratings.addAll(ratingList);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        final View view = inflater.inflate(R.layout.item_rating, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        final Rating rating = ratings.get(position);
        holder.name.setText(actor.getName());
    }

    public void swapItems(List<Rating> ratings) {
        final RatingDiffCallback diffCallback = new RatingDiffCallback(this.ratings, ratings);
        final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);

        this.ratings.clear();
        this.ratings.addAll(ratings);
        diffResult.dispatchUpdatesTo(this);
    }

    @Override
    public int getItemCount() {
        return ratings.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {

        private TextView nameTV;

        public ViewHolder(View itemView) {
            super(itemView);
            nameTV = (TextView) itemView.findViewById(R.id.rating_person_name);
        }
    }
}

 

RatingDiffCallback.class

public class RatingDiffCallback extends DiffUtil.Callback{

    private final List<Rating> oldList;
    private final List<Rating> newList;

    public RatingDiffCallback(List<Rating> oldList, List<Rating> newList) {
        this.oldList = oldList;
        this.newList = newList;
    }

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

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

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        return oldList.get(oldItemPosition).getId() == newList.get(newItemPosition).getId();
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        final Rating oldItem = oldList.get(oldItemPosition);
        final Rating newItem = newList.get(newItemPosition);

        return oldItem.getName().equals(newItem.getName());
    }

    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        // Implement method if you're going to use ItemAnimator
        return super.getChangePayload(oldItemPosition, newItemPosition);
    }
}

 

Let’s understand the RatingDiffCallback.class methods.

getOldListSize()– This method will return the size of the old list.

getNewListSize() This method will return the size of the new list.

areItemsTheSame(int oldItemPosition, int newItemPosition)– This callback method decides whether two objects are representing same items or not.

areContentsTheSame(int oldItemPosition, int newItemPosition)– This callback method decides that two items have same data or not. This method will only be called if the return type is true.

getChangePayload(int oldItemPosition, int newItemPosition)– If areItemTheSame returns true and areContentsTheSame returns false DiffUtil utility calls this method to get a payload about the change.

It is recommended that for a large amount of data in the list, you should do the calculation part in a background thread as it may throw an ANR exception.

Why use DiffUtil?

Here is the performance chart which illustrates that using DiffUtil is better in a case of RecyclerView. These results are based on Nexus 5X with M-

  • 100 items and 10 modifications: avg: 0.39 ms, median: 0.35 ms
  • 100 items and 100 modifications: 3.82 ms, median: 3.75 ms
  • 100 items and 100 modifications without moves: 2.09 ms, median: 2.06 ms
  • 1000 items and 50 modifications: avg: 4.67 ms, median: 4.59 ms
  • 1000 items and 50 modifications without moves: avg: 3.59 ms, median: 3.50 ms
  • 1000 items and 200 modifications: 27.07 ms, median: 26.92 ms
  • 1000 items and 200 modifications without moves: 13.54 ms, median: 13.36 ms

Due to implementation constraints, the max size of the list can be 2^26.

Conclusion

DiffUtil utility class finds the difference between the provided old and new lists and dispatches the updates to the adapter.

I hope you found this useful. For any queries, please drop your comments in the section below.