Android Jetpack Paging and Firebase


RecyclerViews and their Adapters are well known ways of displaying lists of data, but they are not optimal, because they load entire lists into themselves, and upon change, they simply reload the full list (using notifyDataSetChanged).

Let’s imagine the following scenario:

I have a list, containing all the currently living animal species, each has a name and a description. After a quick research, 8 hours of coding and 4 liters of coffee, I manage to create my list that contains 7.7 million Animals. Every time I update the name of a single animal, I must load 7.7M items into the adapter. Furthermore, if I move this database to cloud storage and add pictures to it, I’ll have to download around 7.7GB of data.

A few years ago, the DiffUtil class tried to solve this by loading only the modified items into the adapter, thus making the process faster, but we still had to have the full list to compare it to the modified items. With the new Jetpack library, we can load the subset that is currently visible to the user, and no more.

Android Paged List


The PagedListAdapter comes with different kinds of DataSources (PositionalDataSource, ItemKeyedDataSource, PageKeyedDataSource) for different uses. In this post, we will focus on the ItemKeyedDataSource. Using this we can retrieve “pages”, subsets of a list, starting or ending at a given key.

Classes and responsibilities:

  • Data – “Animals, with name with description”
  • DataSource – Reads the data. If the database changes, this must be invalidated (cache is invalid)
  • DataSourceFactory – Provides DataSources
  • DataProvide – Creates LiveData<PagedList> – s with the given configuration and DataFactory, caches data
  • ViewModel – Link between UI and DataSources
  • PagedListAdapter – Contains and manages the LiveDatas
  • RecyclerView – the View responsible for visualisation

After the Application has started, the function will be called and a LiveData will be created. When the LiveData is observed the Factory will create a new DataSource and load the requested subset of data into a PagedList. Then, trough the ViewModel this Paged Data will be added (submitted) to the PagedListAdapter. Animation will occur and the list will be visible in the RecyclerView.

Android paged list

Scrolling the PagedList will trigger the DataSource, thus additional pages will load. (The PositionalDataSource can show placeholders while the data is loading.)

pagedlist android - paged list adapter

If the database changes, we invalidate the current DataSource and create a new DataSource which loads the requested data into the proper position of a new PagedList, and then passes it to the paged list - firebase pagination

Note that the PagedList and the DataSource travel together.

Project Classes

So, how did I use all this to implement my animal dictionary?

Below you can see the classes I’ve created with a brief behaviour description.

All classes function according to the aforementioned.


Data class, contains two strings: name, description


Subclass of the ItemKeyedDataSource.

The loadInitial(…) function triggers a callback (LoadInitialCallback) with the first few list items, depending on the requestedLoadSize.

The loadAfter(…) and loadBefore(…) loads a give number of items starting/ending at a key.

The getKey(…) returns the key of an item, so we can find it in the list.

Also, this is a DataSource, so this must be invalidated if the database changes. In the init method we subscribe to the dataChanges, and if it happens we invalidate.


A simple DataSource.Factory. In its create() method it returns a new AnimalDataSource.


With its getAnimal() method, we can request a new LiveData<PagedList<Animal>>.

Here we use the LivePagedListBuilder to get the LiveData. We need a configuration and a factory. For the factory, AnimalDataFactory is used. We create a Config isntance. In this we can set the parameters such as: InitialLoadSize, PageSize.


An interface that connects the Provider and the Adapter.


Subclass of PagedListAdapter.

It works like a simple Adapter, but the speciality here is the DiffUtil object. The DiffUtil.ItemCallback checks which items have changed in the newly submitted list. One checks if the item itself has changed (added, removed) and the other checks if the contents have changed. We use this instead of notifyDataSetChagned(). (Make sure you compare the right things!)

RecyclerView & Main

The RecyclerView contains the AnimalAdapter. Also in the Main Class we observe the DataProviders getAnimal() method (returns LiveData). If the LiveData changes (because of the database) the Adapter’s submitList(…) is called with the new LiveData and the list will be “refreshed”.

With this we have finished the “Android part” of the project.


We have to make sure that we can identify each item using some kind of key, but apart from that we can use any kind of structure.

Keep in mind that Firebase uses an ascending order automatically. We have to keep the order of the project to work. I used numbers, but pushing (push()) animals works too, because it generates a key.

With these in mind, I created the following database:database firebase pagination

Firebase Manager

Last but not least, we have to create the Firebase Manager.

The getAnimals() method returns the first “count” number of items from the database.

The loadAfter(…) and loadBefore(…) loads a given number of items starting/ending at a key.

The getAnimalChanges subject is used in the AnimalDataSource. This subject notifies the DataSource if the database changes and invalidation is required.

In the init method we create a listener so if the content changes, we can fetch immediately.


Let’s see the final application!final application - firebase paged list

It’s not big, it’s not complicated, but the potential it has can be the difference between a good or a bad application… and a crowded zoo.

If you want to see more posts like this, follow TeamWanari on Facebook or LinkedIn!

Ádám Hosszú

Ádám Hosszú

Programming is like sex. One mistake and you have to support it for the rest of your life. (Michael Sinz)

Latest posts by Ádám Hosszú (see all)