Exploring Android Jetpack Paging Library
As we know Google has announced the new Jetpack feature for android application development. I have explained a few of them from the last articles like navigation through NavigationUI from Jetpack and this. I really love this Google change. I am continuing with some of the other awesome Jetpack feature libraries whose name is Pagination.
As we all know that day by day people are engaged with mobile applications and data of every application keeps increasing daily and loading the huge data on the screen is very difficult for the developer at a time to maintain the processing the fast and smooth for the user. So, in this case, we need to show the data in the formate for paging, it means we load data in the part, Once the first part data is loaded on the screen then if the user scrolls down then we need to load part2 data, etc.
What is paging and how to use the Paging Library?
Paging is the part of the Android Jetpack library which loads the data from the remote server in the part which makes less load on the server and as well as application. In this case, it will consume less memory. Now the paging library used the Live Data stream feature to observing the change and automatically it gets reflected on recyclerview.
To use the Jetpack paging library we need to add the few dependency files to our build.gradle file.
// Paging implementation "androidx.paging:paging-runtime-ktx:2.1.0"
The paging library used mostly three-component of DataSource which need to understand. Form the official document :
- UseÂ
PageKeyedDataSource
 if pages you load embed next/previous keys. For example, if you’re fetching social media posts from the network, you may need to pass aÂnextPage
 token from one load into a subsequent load. - UseÂ
ItemKeyedDataSource
 if you need to use data from item N to fetch item N+1. For example, if you’re fetching threaded comments for a discussion app, you might need to pass the ID of the last comment to get the contents of the next comment. - UseÂ
PositionalDataSource
 if you need to fetch pages of data from any location you choose in your data store. This class supports requesting a set of data items beginning from whatever location you select. For example, the request might return the 50 data items beginning with location 1500.
Ok, let’s implement this library with the sample movie app to check the data loading into the part from the remote server. Let’s implement the first PageKeyDataSource for remote call and execute the callback result to the page size.
class MovieDataSource : PageKeyedDataSource<Int, Movie>() { override fun loadInitial( params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Movie> ) { val service = ApiClient.buildService(MovieService::class.java) val call = service.getPopularMovies(FIRST_PAGE) call.enqueue(object : Callback<MoviesResponse> { override fun onResponse( call: Call<MoviesResponse>, response: Response<MoviesResponse> ) { if (response.isSuccessful) { val apiResponse = response.body()!! val responseItems = apiResponse.movies responseItems?.let { callback.onResult(responseItems, null, FIRST_PAGE + 1) } } } override fun onFailure(call: Call<MoviesResponse>, t: Throwable) { } }) } override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Movie>) { val service = ApiClient.buildService(MovieService::class.java) val call = service.getPopularMovies(params.key) call.enqueue(object : Callback<MoviesResponse> { override fun onResponse( call: Call<MoviesResponse>, response: Response<MoviesResponse> ) { if (response.isSuccessful) { val apiResponse = response.body()!! val responseItems = apiResponse.movies val key = if (apiResponse.totalPages > params.key) params.key + 1 else apiResponse.totalPages responseItems?.let { callback.onResult(responseItems, key) } } } override fun onFailure(call: Call<MoviesResponse>, t: Throwable) { } }) } override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Movie>) { val service = ApiClient.buildService(MovieService::class.java) val call = service.getPopularMovies(params.key) call.enqueue(object : Callback<MoviesResponse> { override fun onResponse( call: Call<MoviesResponse>, response: Response<MoviesResponse> ) { if (response.isSuccessful) { val apiResponse = response.body()!! val responseItems = apiResponse.movies val key = if (params.key > 1) params.key - 1 else 0 responseItems?.let { callback.onResult(responseItems, key) } } } override fun onFailure(call: Call<MoviesResponse>, t: Throwable) { Log.e("", t.localizedMessage) } }) } companion object { const val FIRST_PAGE = 1 const val PAGE_SIZE = 10 } }
Now, this class should be created inside the DataSource Factory to load the initial data from the remote. Let’s get instantiated and observe the changes to post the data from the Live Data.
class MovieDataSourceFactory : DataSource.Factory<Int, Movie>() { val movieLiveDataSource = MutableLiveData<MovieDataSource>() override fun create(): DataSource<Int, Movie> { val dataSource = MovieDataSource() movieLiveDataSource.postValue(dataSource) return dataSource } }
Now we need to configure the PageList with the PageListBuilder to provide the maximum page size and observe this PageList with the LiveData.
class HomeViewModel : ViewModel() { var moviePageList : LiveData<PagedList<Movie>> private var liveDataSource : LiveData<MovieDataSource> init { val itemDataSourceFactory = MovieDataSourceFactory() liveDataSource = itemDataSourceFactory.movieLiveDataSource val config = PagedList.Config.Builder() .setEnablePlaceholders(false) .setPageSize(MovieDataSource.PAGE_SIZE) .build() moviePageList = LivePagedListBuilder(itemDataSourceFactory, config).build() } }
Ok, all configuration has done here. Now we need to observe this PageList inside the View and submit data to the page adapter to adapt the changes to recylerview.
class MovieAdapter : PagedListAdapter<Movie, MovieAdapter.MovieViewHolder>(MOVIE_COMPARATOR) { var onItemClick: ((Movie) -> Unit)? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_movie, parent, false) return MovieViewHolder(view) } override fun onBindViewHolder(holder: MovieViewHolder, position: Int) { val movie= getItem(position) movie?.let { // update the data to view holder } } }
Inside the View observe the change of PageList Live Data and submit the data to the pager adapter.
val itemViewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java) //observe page list itemViewModel.moviePageList.observe(this, Observer { adapter.submitList(it) })
And that’s it, guys! I hope you enjoyed this article and found it useful. I really like the new Paging library. It gives you a lot of power, does a lot of work for you on the background thread.
WrappingÂ
Now we have a good understanding of the new android Jetpack Component Paging. We have played with a sample project to learn the basic concept of Paging. I personally like this change from Google. You can get the full Github source code of Jetpack Component. So my next tutorial, we will learn some other jetpack Components in detail, ok till then enjoy your healthy day.
If you are wondering to learn Android then Please learn from Android category and wondering to learn Kotlin then Kotlin Category will help you. If you want to learn all the python article, then learn from the python category.
Happy Coding 🙂
I am a very enthusiastic Android developer to build solid Android apps. I have a keen interest in developing for Android and have published apps to the Google Play Store. I always open to learning new technologies. For any help drop us a line anytime at contact@mobologicplus.com