AndroidPub
Published in

AndroidPub

Android DTT #12 — Optimize and Animate Your RecyclerView with DiffUtil

In my last post about adding animation in RecyclerView, there are some drawbacks need to be addressed. The first one is that it requires the data to have an ID of type int/long. This is not always the case, since some of the data have String as the ID, some even doesn’t have one.

The second drawback is notifyDataSetChanged(). It is used to inform my adapter that there’s change in the data list. But it turns out that we should only use that as a last resort, according to Android Developer docs.

If you are writing an adapter it will always be more efficient to use the more specific change events if you can. Rely on notifyDataSetChanged() as a last resort.

Instead, we can use these specific change events:

notifyItemChanged(int)
notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)

But determining what to call based on the data changes is not a simple task.

Goodbye notifyDataSetChanged

Fortunately the team at Google have created a util class which get the job done called DiffUtil.

I’m adding the DiffUtil in the updateItems(List<Data>) method inside the adapter, but you can also use this in the one that holds the adapter (e.g. Activity):

public void updateItems(List<Data> newItems) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new AwesomeCallback(mDataList, newItems));
diffResult.dispatchUpdatesTo(this);

mDataList.clear();
mDataList.addAll(newItems);
}

AwesomeCallback is an implementation of DiffUtil.Callback. It provides information that DiffUtil requires in order to create a chain of updates to the adapter: the size of both old and new list, and also their identity.

areItemsTheSame should return true if two objects are the same. While areContentsTheSame should return true if the content of both objects is all the same.

Try to run the code and this is what we’ll get:

Make It Better

But, isn’t areContentsTheSame is going to return false most of the time?

True. As of current state, the adapter will do Full Binding whenever areItemsAreSame returns true but areContentsTheSame returns false. We can optimize it by checking for the content of the Data one bye one:

@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
Data oldData = oldList.get(oldItemPosition);
Data newData = newList.get(newItemPosition);

return (oldData.getTitle().equals(newData.getTitle()) && oldData.getDescription().equals(newData.getDescription()));
}

With this, when the title and description are the same, the adapter will not call onBindViewHolder since the item and it’s content are identical. We saved the processor few mili-(micro?) seconds. Yay!

Make It Even Better — Partial Binding

Imagine we have a lots of field in our Data class. When the DiffUtil compares two Data they only differ in one or two fields, what can we do to optimize it? Partial Binding.

I said that the adapter will do full binding when the areItemsAreSame returns true but areContentsTheSame returns false. Whenever this condition happens, getChangePayload in the Callback will be called. We can tell the adapter on which field needs to be updated by giving the adapter a payload which we will create in getChangePayload inside our AwesomeCallback.

Don’t use literal string as key, It’s 100x better to use a constant String as the key of the Bundle’s items.

By default, getChangePayload will return null. Which will make the adapter do a full binding. By overriding the method, we can give a payload that can be used in our adapter to only change relevant UI.

The payload will be given to the adapter in onBindViewHolder(ViewHolder, int, List<Object> as the third parameter.

And here’s the final result:

And of course you don’t want to use this kind of animation in your apps. Make something better ;)

It’s worth noting that the diff calculation will be done in UI thread by default. So if you have a big amount of data in the list, I’d suggest that you move it to the background thread and dispatch the updates in the UI thread. There are a lot of articles that will guide you to achieve this. Here’s one:

If you like this, hit that heart button and recommend it to your friends.

ADTT (Android Development Tips and Tricks) is a 31 series of blog posts that I’m trying to finish in throughout May. Click here for index.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store