Optimising data updates to Listview/Recyclerview

Most common approach of updating recyclerview adapters are running loops on adapter data items and if not present. Or replace the entire data set with old+new data set and then call notifyDataSetChanged()/notifyItemRangeInserted()
Something like this below

[RecyclerViewAdapter] []
1
2
3
4
5
6
7
public void updateData(ArrayList<ViewModel> viewModels) {
items.clear();
items.addAll(viewModels);
notifyDataSetChanged();
}

Calling notifyDataSetChanged() or any other functions to update the adapter is a costly process. The reason behind this is the adapter has to invalidate the data set and recreate the views.

A better optimised way of doing this is using DiffUtil which is available in support library >= 24 version. DiffUtil runs on Myser’s difference alogrithm with minimal updates to convert to a new list. It does not handle moving of items within list but runs another iteration to detect where it has moved. Read about it here

Let me just stop talking about it and get on with the code. Diffutil has a callback class that can implemented like this

[DiffCalback] []
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class DiffCalback extends DiffUtil.Callback{
List<ViewModel> oldData;
List<ViewModel> newData;
public MyDiffCallback(List<ViewModel> newData, List<ViewModel> oldData) {
this.newData = newData;
this.oldData = oldData;
}
@Override
public int getOldListSize() {
return oldData.size();
}
@Override
public int getNewListSize() {
return newData.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldData.get(oldItemPosition).id == newData.get(newItemPosition).id; // your data model will have atomic id value.
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldData.get(oldItemPosition).equals(newData.get(newItemPosition));
}
}

The DiffUtl.calculateDiff() takes this callback and a boolean asking if items are movable in list. So pass this callback to it in your adapter

[Adapter] []
1
2
3
4
5
public void updateData(ArrayList<ViewModel> viewModels) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCalback(viewModels,oldData));
}

Now the only part left is dispatching the new updated list to the adapter through the list callback. Which can be implemented like this

[Adapter] []
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private ListUpdateCallback listUpdateCallback = new ListUpdateCallback() {
@Override
public void onInserted(int position, int count) {
// custom observer methods
notifyItemRangeInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
// custom observer methods
notifyItemRangeRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
// custom observer methods
notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count, Object payload) {
// custom observer methods
notifyItemRangeChanged(position, count);
}
};

Now diffresult is propogated to the adapter like below

[Adapter] []
1
2
3
4
5
6
public void updateData(ArrayList<ViewModel> viewModels) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCalback(viewModels,oldData));
diffResult.dispatchUpdatesTo(listUpdateCallback);
}

Always exploit ways to better optmize list creations for seamless user experience.