Clean, Easy & New- How To Architect Your App: Part 5 — List Update

with Paging Library’s ListAdapter

Britt Barak
ProAndroidDev

--

Back to my venues demo app shown on previous posts (part 4, part 3, also check out part 2 and part1 which explains more about the structure) .

As you already know, I use Foursquare API which allows me to create a network call and get the meta data for the venues that fit my search. However, in order to get a venue’s photos, I have to create another api call, one per venue.

Once a photo url returns →

I should update the venues data →

which should update the venue view models →

which should update the UI accordingly → on our case: load the new image.

One great way to update the UI list is to use Paging library (which nicely uses DiffUtil under the hood)

Paging library is a powerful tool to handle large datasets. Here I’ll show an example of how to use just a specific part of it for finding diffs between list items and updating the UI efficiently. We won’t be using here a DataSource and paging of the data for now.

Providing a new list of items, Paging library finds the differences between the new list, and the list which is currently presented on they screen, and updates the UI.

What’s efficient about its work?

  • It calculates the diffs on a background thread, so it won’t slow the UI thread by any chance.
  • It notifies a RecyclerView.Adapter which specific item had changed. That way, we don’t draw the list from scratch (as if we would have used notifyDataSetChanged()) but only the needed items.

It is so simple to use, elegant and quite performant, that you should definitely consider using it whenever it fits!

From the documentation: “The Paging Library components do most of their work in a background thread, so they don’t burden the UI thread.”

How to use it?

0. Add the library

First, remember to add it to your project, as it’s separate from the other architecture components I used on previous posts. Check it out here.

1. Extend ListAdapter

class VenuesAdapter extends ListAdapter<VenueViewModel, VenueViewHolder> 

2. Create DiffCallback:

DIFF_CALLBACK = new DiffCallback<VenueViewModel>() {
@Override
public boolean areItemsTheSame(
VenueViewModel oldVenue, VenueViewModel newVenue) {
//TODO
}

@Override
public boolean areContentsTheSame(
VenueViewModel oldVenue, VenueViewModel newVenue) {
//TODO
}
};

All you should implement are 2 methods, which honestly are pretty self explanatory: areItemsTheSame() and areContentsTheSame().

They both take an item from the old list (the one which is currently presented), and an item from the new list. They will allow us to realize if:

  • The items represent the same objects but content is changed (e.g. it’s the same venue, only now with has an image url available). This case bindViewHolder() can be called.
  • The items represent 2 completely different objects (e.g. 2 different venues). This case createViewHolder() might be needed.
  • The items are the same and haven’t changed so we don’t need to change anything in the UI.

2.1. Implement areItemsTheSame()

Usually will be done by checking if the items IDs are equal or not.

@Override
public boolean areItemsTheSame(
VenueViewModel oldVenue, VenueViewModel newVenue) {
return Objects.equals(oldVenue.getId() ,newVenue.getId());
}

2.2 Implement areContentsTheSame()

Can be elegantly done with an equals() method you should implement for the view model POJO.

@Override
public boolean areContentsTheSame(
VenueViewModel oldVenue, VenueViewModel newVenue) {
return oldVenue.equals(newVenue);
}

2.3. Implement equals() on the POJO:

For the sake of my example:

VenueViewModel.java: public boolean equals(VenueViewModel other) {
if (other == null) return false;
return (Objects.equals(this.id, other.id))
&& (Objects.equals(this.name, other.name))
&& (Objects.equals(this.imageUrl, other.imageUrl));
}

3. Give DiffCallback to adapter’s constructor

public VenuesAdapter() {
super(DIFF_CALLBACK);
}

4. Set new list items:

Whenever adapter.setList() is called, the adapter starts working, calculating the diffs and updating the UI.

When exactly do we call it?

Repository has new data → Use case is notified on the data → Use case updates the view model → UI is notified on the new view model → UI updates the view.

Specifically: My UI (in my case my MainActivity) observes venues (which is a LiveData object):

viewModel.venues
.observe(this, list -> adapter.setList(list));

That’s it!

Whenever the view model changes, the magic will happen for you. Super easy and recommended.

If in the future I’ll use larger datasets, I can switch my RecyclerView to PagedList, and get more advantaged of the powerful PagingLibrary.

In an upcoming post I’ll show another way to update specific items yourself, if you really really really don’t want to use diffing. But honestly, for most cases- this is a really great solution!

Thanks for reading! ❤️👏😍

--

--

Product manager @ Facebook. Formerly: DevRel / DevX ; Google Developer Expert; Women Techmakers Israel community lead.