Hi everyone, In this article, we will learn to implement Paging support for Firestore Database in Android. Before starting to the topic, Let’s first take a look at the available components within the Firebase.
FirebaseUI-Android library has FirestoreRecyclerAdapter for easy implementation of the population of Firestore Database. But if the database is having a total number of children in thousands or around then it becomes a bad presentation of User Interface. Let’s take an example if you are implementing social media app and you are having around 100 Posts. If we load these Posts using FirestoreRecyclerAdapter then it will load all the Posts at the time of loading. So, this will be wastage of memory or hectic for the user to scroll down with a large list or it is not good to present in front of the application user. To overcome this, we will use pagination which will load Firestore Database document items in pages.
This API is available on this official FirebaseUI ’s GitHub repository.
FirestorePagingAdapter
— binds a Query
to a RecyclerView
by loading data in pages. Best used with large, static data sets. Real-time events are not respected by this adapter, so it will not detect new/removed items or changes to items already loaded. The FirestorePagingAdapter
is built on top of the Android Paging Support Library.
See Output:
💻 Getting Started :
Let’s get started to the code!
Open Android Studio. Create a new project OR you can simply clone this repository:
First of all, go to Firebase Console and create a new Android Project. Download configuration file i.e. google-services.json
and place it in the /app directory.
In this app, you are showing a paginated list of Posts. Posts will load inRecyclerView.
Gradle Setup
In the app module ofbuild.gradle
include following dependencies.
// RecyclerView
implementation 'androidx.recyclerview:recyclerview:1.0.0'
// Firebase & Firestore SDK
implementation 'com.google.firebase:firebase-core:17.0.1'
implementation 'com.google.firebase:firebase-firestore:20.1.0'
// Firestore FirebaseUI Library
implementation 'com.firebaseui:firebase-ui-firestore:5.0.0'
// Paging Library
implementation 'android.arch.paging:runtime:1.0.1'
App Setup
Make model class (ConsiderPost.java
) in the app.
data class Post(
var authorName: String? = null,
var message: String? = null
)
Then, create aViewHolder
class by inheritingRecyclerView.ViewHolder
as below.
class PostViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private var authorView: TextView = itemView.findViewById(R.id.post_AuthorName)
private var messageView: TextView = itemView.findViewById(R.id.post_Message)
fun bind(post: Post) {
authorView.text = post.authorName
messageView.text = post.message
}
}
Initialize :
Don’t forget to set LayoutManager
to the RecyclerView.\
Set it usingRecyclerView#setLayoutManager()
.
Setup Configuration for PagedList
First of all configure PagedList\
Remember that, the size you will pass to setPageSize()
a method will load x3 items of that size at first load. (Here, in this example we passed value 10. So, it will load 10x3 i.e. 30 items at first load).
// Init Paging Configuration
val config = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(2)
.setPageSize(10)
.build()
Then Configure Adapter by building FirestorePagingOptions. It will generic.\
Remember one thing, This query should only contain where()
and orderBy()
clauses. Any limit()
or pagination clauses will cause errors.
// Init Adapter Configuration
val options = FirestorePagingOptions.Builder<Post>()
.setLifecycleOwner(this)
.setQuery(mQuery, config, Post::class.java)
.build()
Initialize Adapter
FirestorePagingAdapter
is built on the top of Android Architecture Components - Paging Support Library. To implement, you should already have RecyclerView.ViewHolder
subclass. Here We used PostViewHolder
class.
// Instantiate Paging Adapter
mAdapter = object : FirestorePagingAdapter<Post, PostViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val view = layoutInflater.inflate(R.layout.item_post, parent, false)
return PostViewHolder(view)
}
override fun onBindViewHolder(viewHolder: PostViewHolder, position: Int, post: Post) {
// Bind to ViewHolder
viewHolder.bind(post)
}
override fun onError(e: Exception) {
super.onError(e)
Log.e("MainActivity", e.message)
}
override fun onLoadingStateChanged(state: LoadingState) {
when (state) {
LoadingState.LOADING_INITIAL -> {
swipeRefreshLayout.isRefreshing = true
}
LoadingState.LOADING_MORE -> {
swipeRefreshLayout.isRefreshing = true
}
LoadingState.LOADED -> {
swipeRefreshLayout.isRefreshing = false
}
LoadingState.ERROR -> {
Toast.makeText(
applicationContext,
"Error Occurred!",
Toast.LENGTH_SHORT
).show()
swipeRefreshLayout.isRefreshing = false
}
LoadingState.FINISHED -> {
swipeRefreshLayout.isRefreshing = false
}
}
}
}
Any changes that occur in the adapter will result in the callback onLoadingStateChanged()
Error Handling
To get to know about Errors caught during Paging, Override method onError()
in the adapter.
override fun onError(e: Exception) {
super.onError(e)
Log.e("MainActivity", e.message)
// Handle the Error.
}
Retrying List (After Error / Failure)
To retry items loading in RecyclerView, retry()
method from Adapter class is used.\
Use it asFirestorePagingAdapter#retry()
.\
This method should be used only after caught in Error. retry()
should not be invoked anytime other than ERROR state.\
Whenever LoadingState
becomes LoadingState.ERROR
we can useretry()
to load items in RecyclerView which were unable to load due to recent failure/error and to maintain Paging List stable.\
See the demo for a method.
mAdapter.retry();
Refreshing List
To refresh items in RecyclerView, refresh()
method from Adapter class is used.\
Use it as FirestorePagingAdapter#refresh()
.\
This method clears all the items in RecyclerView and reloads the data again from the beginning.\
See the demo for a method.
// Refresh Action on Swipe Refresh Layout
swipeRefreshLayout.setOnRefreshListener {
mAdapter.refresh()
}
Set Adapter
Finally, Set adapter to theRecyclerView
.
recyclerView.adapter = mAdapter
Lifecycle
At last, To begin populating data, callstartListening()
method.stopListening()
stops the data being loaded.
To begin populating data, call the startListening()
method. You may want to call this in your onStart()
method. Make sure you have finished any authentication necessary to read the data before calling startListening()
or your query will fail.
override fun onStart() {
super.onStart()
mAdapter.startListening()
}
Similarly, the stopListening()
call freezes the data in the RecyclerView
and prevents any future loading of data pages.
Call this method when the containing Activity or Fragment stops:
Stop Lifecycle of the Adapter
Thus, we have implemented the FirestoreRecycler Pagination. 😃
You can see the full app demo on below-listed resources with source code and step-by-step guide.
Please have a clap for this article if you found it helpful!
Thank You!😃