Architecture Components

Android Paging Library

Welcome to implementing paging library. In this post, I’m going to discuss what is the paging library used for? How does it work? what are the advantages? and will learn implementation step of android paging library. So let’s get started

Introduction

What is paging and What is need of paging library. As you see in figure on right side represent a RecyclerView. Paging does, meaning the loading data for list in piecemeal. Basically, loading 3 items at a time in this image. We could load 10 at a time or 50 at a time. If you load all 1000 items once it will very slow and smash the user experience.

Android paging library makes it easy to load data gradually and gracefully within our app’s RecyclerView. It helps in loading and displaying data efficiently in chunk.

Paging Library Example

In this diagram, File have to be data source and from the data source you can do anythings such as call web API or database call, we can do anythings to get data. From that we get the PagedList after that creates page list adapter, which than populate our RecyclerView

Advantages of Paging Library

  • Paging library data request consumes less network bandwidth and fewer system resources. So the user needed a small data plan will appreciate a data conscious app .
  • App gives quick response even during the data update and refresh and also continue to respond quickly to the user input.

Technology Used

Following technology, we are using in this android app tutorials. Prerequisites are, you have to knowledge of below android component.

Step to implement Android Paging Library

  • Project Creation & Set Up
  • Create Models
  • Set Up Retrofit Client
  • Create a Data Source
  • Create a DataSource Factory
  • Setup ViewModel
  • Create a PagedListAdapter
  • Add Paging Stuff in MainActivity
  • Conclusion   

1. Project Creation & Set Up

Let’s move to Android Studio and create a Java/Kotlin android project. After project creation, You have to add certain dependency for this android app tutorial.

1.1 Let’s open the app level gradle file and add below dependencies

Using Paging, first of all, we need to add paging library dependency in gradle. Apart from this you need to some more dependencies such as LiveData, Retrofit etc.

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.example.paginglibrary"
        minSdkVersion 15
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

def paging_version = "1.0.1"
def glide_version = "4.9.0"
def constraint_version = "1.1.3"
def retrofit_version = "2.4.0"
def appcompat_version = "1.1.0"

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "androidx.appcompat:appcompat:$appcompat_version"
    implementation "androidx.constraintlayout:constraintlayout:$constraint_version"
    implementation "com.github.bumptech.glide:glide:$glide_version"

    // paging library
    implementation "android.arch.paging:runtime:$paging_version"

    // retrofit
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

    // Logging
    implementation 'com.squareup.okhttp3:logging-interceptor:3.14.2'

    implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'com.google.android.material:material:1.1.0-alpha10'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'

}
apply plugin: 'com.android.application'

android {
  compileSdkVersion 29
  buildToolsVersion "29.0.0"
  defaultConfig {
    applicationId "com.example.paginglibrary"
    minSdkVersion 15
    targetSdkVersion 29
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  }
  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
  compileOptions {
    sourceCompatibility 1.8
    targetCompatibility 1.8
  }
}

def paging_version = "1.0.1"
def glide_version = "4.9.0"
def constraint_version = "1.1.3"
def retrofit_version = "2.4.0"
def appcompat_version = "1.1.0"

dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation 'androidx.appcompat:appcompat:1.1.0'
  implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

  implementation "androidx.appcompat:appcompat:$appcompat_version"
  implementation "androidx.constraintlayout:constraintlayout:$constraint_version"
  implementation "com.github.bumptech.glide:glide:$glide_version"

  // paging library
  implementation "android.arch.paging:runtime:$paging_version"

  // retrofit
  implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
  implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

  // Logging
  implementation 'com.squareup.okhttp3:logging-interceptor:3.14.2'

  implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04'
  implementation 'androidx.cardview:cardview:1.0.0'
  implementation 'com.google.android.material:material:1.1.0-alpha10'

  testImplementation 'junit:junit:4.12'
  androidTestImplementation 'androidx.test:runner:1.2.0'
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
  implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
}
1.2 Add Internet Permission

In this android app tutorial we fetch data from server, so let’s add INTERNET uses permission in AndroidManifest.

  <uses-permission android:name="android.permission.INTERNET"/>

2. Let’s create a model class

In this tutorial, we get data from server. The JSON model is below

{
  "page": 1,
  "per_page": 6,
  "total": 12,
  "total_pages": 2,
  "data": [
    {
      "id": 1,
      "email": "george.bluth@reqres.in",
      "first_name": "George",
      "last_name": "Bluth",
      "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
    },
    {
      "id": 2,
      "email": "janet.weaver@reqres.in",
      "first_name": "Janet",
      "last_name": "Weaver",
      "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
    }
  ]
}
2. Let’s create a model class for parsing JSON payload

I’m creating a models class one for upper JSON object second for inner data JSON object. Let’s creates a class for an upper model class named is UserResponse.

package com.example.paginglibrary.model

import com.google.gson.annotations.SerializedName

class User {

    @field:SerializedName("avatar")
    var avatar: String? = null
    @field:SerializedName("email")
    var email: String? = null
    @field:SerializedName("first_name")
    var firstName: String? = null
    @field:SerializedName("id")
    var id: Long? = null
    @field:SerializedName("last_name")
    var lastName: String? = null
}

class UserResponse {

    @field:SerializedName("data")
    var users: List<User>? = null
    @field:SerializedName("page")
    var page: Int = 0
    @field:SerializedName("per_page")
    var perPage: Long = 0
    @field:SerializedName("total")
    var total: Long = 0
    @field:SerializedName("total_pages")
    var totalPages: Int = 0

}
package com.example.paginglibrary.model;

import com.google.gson.annotations.SerializedName;
import java.util.List;

public class UserResponse {

  @SerializedName("data")
  private List<User> users;
  @SerializedName("page")
  private Long page;
  @SerializedName("per_page")
  private Long perPage;
  @SerializedName("total")
  private Long total;
  @SerializedName("total_pages")
  private Long totalPages;

  public List<User> getUsers() {
    return users;
  }

  public void setUsers(List<User> users) {
    this.users = users;
  }

  public Long getPage() {
    return page;
  }

  public void setPage(Long page) {
    this.page = page;
  }

  public Long getPerPage() {
    return perPage;
  }

  public void setPerPage(Long perPage) {
    this.perPage = perPage;
  }

  public Long getTotal() {
    return total;
  }

  public void setTotal(Long total) {
    this.total = total;
  }

  public Long getTotalPages() {
    return totalPages;
  }

  public void setTotalPages(Long totalPages) {
    this.totalPages = totalPages;
  }
}
// User class 

package com.example.paginglibrary.model;

import com.google.gson.annotations.SerializedName;

public class User {

  @SerializedName("avatar")
  private String avatar;
  @SerializedName("email")
  private String email;
  @SerializedName("first_name")
  private String firstName;
  @SerializedName("id")
  private Long id;
  @SerializedName("last_name")
  private String lastName;

  public String getAvatar() {
    return avatar;
  }

  public void setAvatar(String avatar) {
    this.avatar = avatar;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

3. Set Up Retrofit Client

As you know retrofit is most commonly used network services in android. We have to set up retrofit client for this app also. Let’s go ahead

3.1 Retrofit Interface

This should be pretty self-explanatory. We are creating service interface named is RestApiService and expose get request methods.

package com.example.paginglibrary.network

import com.example.paginglibrary.model.UserResponse
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query

/**
 * Created by Morris on 03,June,2019
 */
interface ApiService {

    @GET("users")
    fun getUsers(@Query("page") page: Int): Call<UserResponse>
}
package com.example.paginglibrary.network;

import com.example.paginglibrary.model.UserResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface ApiService {
  @GET("users")
  Call<UserResponse> getUsers(@Query("page") long page);
}
3.2 Retrofit Service Builder

Your interface is ready to use, let’s create a web service builder class. To do that create a class named is ApiServiceBuilder.

package com.example.paginglibrary.network

import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object ApiServiceBuilder {

    // Base URL
    private const val URL = "https://reqres.in/api/"

    // Create Logger
    private val logger = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)

    // Create OkHttp Client
    private val okHttp = OkHttpClient.Builder()
        .addInterceptor(logger)

    // Create Retrofit Builder
    private val builder = Retrofit.Builder()
        .baseUrl(URL)
        .addConverterFactory(GsonConverterFactory.create())
        .client(okHttp.build())

    // Create Retrofit Instance
    private val retrofit = builder.build()

    fun <T> buildService(serviceType: Class<T>): T {
        return retrofit.create(serviceType)
    }
}
package com.example.paginglibrary.network;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ApiServiceBuilder {
  // Base URL
  private static final String URL = "https://reqres.in/api/";

  // Create Logger
  private static HttpLoggingInterceptor logger =
      new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);

  // Create OkHttp Client
  private static OkHttpClient.Builder okHttp = new OkHttpClient.Builder().addInterceptor(logger);

  // Create Retrofit Builder
  private static Retrofit.Builder builder = new Retrofit.Builder()
      .baseUrl(URL)
      .addConverterFactory(GsonConverterFactory.create())
      .client(okHttp.build());

  // Create Retrofit Instance
  private static Retrofit retrofit = builder.build();

  public static <T> T buildService(Class<T> type) {
    return retrofit.create(type);
  }
}

4. Create a Data Source

Data Source is Incremental data loader for page-keyed content, where requests return keys for next/previous pages. Implement a DataSource using PageKeyedDataSource if you need to use data from page {@code N – 1} to load page {@code N}. This is common, for example, in network APIs that include a next/previous link or key with each page load. In paging library the data source class act as a repository.

In data source, we have to override 3 methods.

  • loadInitial – It loads the first page of your data that use to initialize RecyclerView
  • loadBefore – it used when to scroll up
  • loadAfter – is called when natural scroll down

That is essential needed for a paged key DataSource

package com.example.paginglibrary.mo