<p>This post is about Android Jetpack Paging 3 library and how we can use it to load the large set of data from network using Retrofit in our RecyclerView efficiently with the help of <strong>AndroidPagingExp</strong> application.</p> 
 
 
 
<div class="wp-block-buttons is-content-justification-center is-layout-flex wp-block-buttons-is-layout-flex"> 
<div class="wp-block-button"><a class="wp-block-button__link has-white-color has-text-color has-background" style="background-color: #14066f;" href="https://github.com/arunk7839/JetpackPaging3ExpUsingRetrofit" target="_blank" rel="noreferrer noopener"><strong>DOWNLOAD CODE</strong></a></div> 
</div> 
 
 
 
<h4 class="wp-block-heading"> </h4> 
<p><amp-youtube layout="responsive" width="1200" height="675" data-videoid="h68dN7JsMd4" title="Android Paging 3 Example Using Retrofit"><a placeholder href="https://youtu.be/h68dN7JsMd4"><img src="https://i.ytimg.com/vi/h68dN7JsMd4/hqdefault.jpg" layout="fill" object-fit="cover" alt="Android Paging 3 Example Using Retrofit"></a></amp-youtube></p> 
<h4><span style="color: #000080;"><strong>Android Paging </strong></span></h4> 
 
 
 
<p>The <strong><span style="color: #008000;">Paging</span></strong> library helps you load and display pages of data or small chunks of data at a time from a larger dataset from local storage or over network. Loading partial data on demand reduces usage of network bandwidth and system resources.</p> 
 
 
 
<p> It integrates cleanly with other Jetpack components, and provide first-class Kotlin support.</p> 
 
 
 
<h4 class="wp-block-heading" id="paging"><strong><span class="devsite-heading" style="color: #000080;" role="heading" aria-level="2">Benefits of using the Paging library</span></strong></h4> 
 
 
 
<p>The Paging library includes the following features:</p> 
 
 
 
<ul class="wp-block-list"> 
<li>Provides in-memory caching of the paged data that assures the systematic use of device resources.</li> 
<li>Prevents duplication of the API request, ensuring that your app uses network bandwidth and system resources efficiently.</li> 
<li>Configurable RecyclerView adapters that automatically request data as the user scrolls toward the end of the loaded data.</li> 
<li>Finest support for Kotlin coroutines and Flow, as well as LiveData and RxJava.</li> 
<li>Built-in functionality to add loading state headers, footers, and list separators.</li> 
<li>Built-in support for error handling, including refresh and retry capabilities.</li> 
</ul> 
 
 
 
<h4 class="wp-block-heading"><span style="color: #000080;"><strong>Paging Library Components</strong></span></h4> 
 
 
 
<p> To implement “infinite scroll” functionality , we will use the following Paging library components:</p> 
 
 
 
<ul class="wp-block-list"> 
<li id="7a75" class="hq hr dm hs b ht hu hv hw hx hy hz ia ib ic id ie if ig ih ii ij ik il im in lf lg lh ej" data-selectable-paragraph=""><span style="color: #0000ff;"><strong class="hs io">PagingData</strong></span> : A container for paginated data. It connects the <span style="color: #008000;"><strong>ViewModel</strong></span> layer to the <span style="color: #008000;"><strong>UI</strong></span>. </li> 
<li id="6ec3" class="hq hr dm hs b ht lm hv hw hx ln hz ia ib lo id ie if lp ih ii ij lq il im in lf lg lh ej" data-selectable-paragraph=""><span style="color: #0000ff;"><strong class="hs io">PagingSource</strong></span> : PagingSource object defines a source of data and how to retrieve data from that source. A PagingSource object can load data from any single source, including network sources and local databases.</li> 
<li class="hq hr dm hs b ht lm hv hw hx ln hz ia ib lo id ie if lp ih ii ij lq il im in lf lg lh ej" data-selectable-paragraph=""><strong class="hs io"><span style="color: #0000ff;">Pager.flow</span> </strong>: builds a <span style="color: #008000;"><strong>Flow<;PagingData>;</strong></span>.</li> 
<li class="hq hr dm hs b ht lm hv hw hx ln hz ia ib lo id ie if lp ih ii ij lq il im in lf lg lh ej" data-selectable-paragraph=""><span style="color: #0000ff;"><strong class="hs io">PagingDataAdapter</strong></span> : a <span style="color: #008000;"><strong>RecyclerView.Adapter</strong> </span>that presents <span style="color: #008000;"><strong>PagingData</strong></span> in a <strong><span style="color: #008000;">RecyclerView</span></strong>. </li> 
</ul> 
 
 
 
<div class="wp-block-image"> 
<figure class="aligncenter"><img class="wp-image-2894 aligncenter" src="https://c1ctech.com/wp-content/uploads/2021/10/paging.png" alt="" /></figure> 
</div> 
 
 
 
<h4 class="wp-block-heading"> </h4> 
<h4><strong><span style="color: #000080;">Creating new Project</span></strong></h4> 
 
 
 
<p>1 . Create a new project by going to <strong><span style="color: #008000;">File ⇒ New</span> </strong>Android Project, select <span style="color: #008000;"><strong>Empty</strong></span> Activity, provide <span style="color: #008000;"><strong>app</strong></span> name, select language to <span style="color: #008000;"><strong>kotlin</strong></span> and then finally click on <strong><span style="color: #008000;">finish</span></strong>.</p> 
 
 
 
 
 
<p>2 . Open app-level <span style="color: #008000;"><strong>build.gradle</strong></span> file, add the below dependencies as shown below, and then <strong><span style="color: #0000ff;">sync</span></strong> the project.</p> 
<p><span style="color: #0000ff;"><strong>build.gradle</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted">dependencies { 
<strong><span style="color: #008000;"> // Android Jetpack Paging 3.0 
</span></strong> implementation "androidx.paging:paging-runtime:3.0.0-alpha06" 
 
<strong><span style="color: #008000;"> // ViewModel 
</span></strong> implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" 
 
<span style="color: #008000;"><strong> // Retrofit 
</strong></span> implementation 'com.squareup.retrofit2:retrofit:2.9.0' 
 implementation 'com.squareup.retrofit2:converter-gson:2.4.0' <span style="color: #008000;"><strong>// Gson support adapter for Retrofit</strong></span> 
 implementation "com.squareup.okhttp3:logging-interceptor:4.9.0" <strong><span style="color: #008000;">//OKHttp Interceptor 
</span></strong> 
<strong><span style="color: #008000;"> //Kotlin Coroutines 
</span></strong> implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" 
 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" 
 
<strong><span style="color: #008000;"> //Glide to load images from URL 
</span></strong> implementation 'com.github.bumptech.glide:glide:4.11.0' 
 implementation 'androidx.databinding:databinding-runtime:7.0.3' 
 annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' 
}</pre> 
 
 
 
<p>3. Inside <strong><span style="color: #008000;">android{}</span> </strong>block enable <strong><span style="color: #008000;">viewBinding</span></strong>.</p> 
 
 
 
<pre class="wp-block-preformatted">viewBinding { 
 <strong><span style="color: #008000;"> enabled true</span></strong> 
}</pre> 
 
 
 
<p>4. Go to <span style="color: #008000;"><strong>AndroidManifest.xml</strong></span> and add the internet permission above <span style="color: #008000;"><strong><;application>;</strong></span> tag as shown below:</p> 
 
 
 
<p><span style="color: #0000ff;"><strong>AndroidManifest.xml</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted"><;uses-permission android:name="android.permission.INTERNET" />;</pre> 
 
 
 
 
 
<h4 class="wp-block-heading"><span style="color: #000080;"><strong>Setting Up Retrofit</strong></span></h4> 
 
 
 
<p>Now let’s setup our Retrofit that will hit the backend API to fetch data sets.</p> 
 
 
 
<h5 class="wp-block-heading"><span style="color: #000080;"><strong>Backend API</strong></span></h5> 
 
 
 
<p>In our project, we will use the below free JSON API.</p> 
 
 
 
<pre class="wp-block-preformatted">https://api.instantwebtools.net/v1/passenger?page=0&;size=10</pre> 
 
 
 
<h5 class="wp-block-heading"><span style="color: #000080;"><strong>Creating API Interface</strong></span></h5> 
 
 
 
<p>Create an interface named <span style="color: #008000;"><strong>MyApi</strong></span> and write the following code.</p> 
<p><span style="color: #0000ff;"><strong>MyApi.kt</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted">interface MyApi { 
 
 @GET("passenger") 
 suspend fun getPassengersData( 
 @Query("page") page: Int, 
 @Query("size") size: Int = 10 
 ): PassengersResponse 
 
 companion object { 
 
 private const val BASE_URL = "https://api.instantwebtools.net/v1/" 
 
 operator fun invoke(): MyApi = Retrofit.Builder() 
 .baseUrl(BASE_URL) 
 .addConverterFactory(GsonConverterFactory.create()) 
 .build() 
 .create(MyApi::class.java) 
 } 
}</pre> 
 
 
 
 
 
<p><strong><span style="color: #0000ff;">getPassangersData()</span></strong> : A function which will hit a <span style="color: #008000;"><strong>GET</strong></span> request using the query parameter <strong>page</strong> and <strong>size</strong>.</p> 
 
 
 
<p><span style="color: #0000ff;"><strong>companion object{ } </strong></span>: Inside this, we have the base URL and the invoke function that will return us the <strong>MyApi</strong> Instance.</p> 
 
 
 
<h5 class="wp-block-heading"><span style="color: #000080;"><strong><span id="Data-Classes-to-Map-API-Response">Data Classes to Map API Response</span></strong></span></h5> 
 
 
 
<p>Now, we have to create the data class for the following JSON output which we get as the response.</p> 
 
 
 
<pre class="wp-block-preformatted">{ 
 "totalPassengers": 19851, 
 "totalPages": 19851, 
 "data": [ 
 { 
 "_id": "5fec58acc9a1dc9a93bca8c2", 
 "name": "John Doe", 
 "trips": 500, 
 "airline": [ 
 { 
 "id": 8, 
 "name": "Thai Airways", 
 "country": "Thailand", 
 "logo": "https://upload.wikimedia.org/wikipedia/en/thumb/5/58/Thai_Airways_Logo.svg/200px-Thai_Airways_Logo.svg.png", 
 "slogan": "Smooth as Silk / I Fly THAI", 
 "head_quaters": "Jom Phol Subdistrict, Chatuchak, Bangkok, Thailand", 
 "website": "www.thaiairways.com", 
 "established": "1960" 
 } 
 ], 
 "__v": 0 
 } 
 ] 
}</pre> 
 
 
 
<p>Given below are the corresponding data classes for the above JSON response.</p> 
 
 
 
<ul> 
<li>The <span style="color: #008000;"><strong>PassengersResponse</strong></span> data class looks like,</li> 
</ul> 
<p><span style="color: #0000ff;"><strong>PassengersResponse.kt</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted">data class PassengersResponse( 
 val `data`: List<;Passenger>;, 
 val totalPages: Int, 
 val totalPassengers: In 
)</pre> 
 
 
 
<ul> 
<li>The <span style="color: #008000;"><strong>Passenger</strong></span> data class looks like,</li> 
</ul> 
<p><span style="color: #0000ff;"><strong>Passenger.kt</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted">data class Passenger( 
 val __v: Int, 
 val _id: String, 
 val airline: List<;Airline>;, 
 val name: String, 
 val trips: Int 
)</pre> 
 
 
 
<ul> 
<li>The <span style="color: #008000;"><strong>Airline</strong></span> data class looks like,</li> 
</ul> 
<p><span style="color: #0000ff;"><strong>Airline.kt</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted">data class Airline( 
 val country: String, 
 val established: String, 
 val head_quaters: String, 
 val id: Int, 
 val logo: String, 
 val name: String, 
 val slogan: String, 
 val website: String 
)</pre> 
 
 
 
 
 
<h4 class="wp-block-heading"><span style="color: #000080;"><strong class="id iz">PagingSource</strong></span></h4> 
 
 
 
<p>A <strong><span style="color: #008000;">PagingSource</span></strong> class is used to access data from the backend API.</p> 
 
 
 
<p>The <strong><span style="color: #008000;">PassengersDataSource</span></strong> class extends PagingSource class and takes a primary constructor parameter <span style="color: #008000;"><strong>MyApi</strong></span>.</p> 
 
 
 
<p><strong><span style="color: #0000ff;">PassengersDataSource.kt</span></strong></p> 
 
 
 
<pre class="wp-block-preformatted">class PassengersDataSource(private val api: MyApi) : PagingSource<;Int, Passenger>;() { 
 
 override suspend fun load(params: LoadParams<;Int>;): LoadResult<;Int, Passenger>; {} 
 
}</pre> 
 
 
 
<ul class="wp-block-list"> 
<li>PagingSource takes two parameters, a key and a value. 
<ul> 
<li><strong><span style="color: #0000ff;">key</span></strong>: what data to load. E.g. <span style="color: #008000;"><strong>Int</strong></span> as a page number or <span style="color: #008000;"><strong>String</strong></span> as a next page token.</li> 
<li><strong><span style="color: #0000ff;">value</span></strong>: type of data<span style="color: #008000;"><strong> (Passenger)</strong></span> that will be loaded.</li> 
</ul> 
</li> 
<li>The <span style="color: #0000ff;"><strong>load()</strong></span> function returns a <span style="color: #008000;"><strong>LoadResult</strong></span> and should be implemented to retrieve data from the data source (network in our case).</li> 
<li><span style="color: #000000;">load()</span> is a <strong><span style="color: #0000ff;">suspend</span></strong> function, so you can call other suspend functions here, such as the network call.</li> 
</ul> 
 
 
 
<p>Now, we update the load function like:</p> 
 
 
 
<pre class="wp-block-preformatted"> override suspend fun load(params: LoadParams<;Int>;): LoadResult<;Int, Passenger>; { 
 return try { 
 val nextPageNumber = params.key ?: 0 
 val response = api.getPassengersData(nextPageNumber) 
 
 LoadResult.Page( 
 data = response.data, 
 prevKey = if (nextPageNumber >; 0) nextPageNumber - 1 else null, 
 nextKey = if (nextPageNumber <; response.totalPages) nextPageNumber + 1 else null 
 ) 
 } catch (e: Exception) { 
 LoadResult.Error(e) 
 } 
 } 
}</pre> 
 
 
 
<p><span style="color: #008000;"><strong>LoadResult</strong></span> can take one of the following types:</p>
<!-- WP QUADS Content Ad Plugin v. 2.0.98.1 -->
<div class="quads-location quads-ad2" id="quads-ad2" style="float:none;margin:0px;">

</div>
 
 
 
 
<ul> 
<li><strong><span style="color: #0000ff;">LoadResult.Page:</span></strong><span style="color: #0000ff;"><span style="color: #000000;"> If the result is successful, return the loaded data wrapped in a <strong>LoadResult.Page</strong> object together with information about next and previous keys.</span></span> 
 
</li> 
<li><strong><span style="color: #0000ff;">LoadResult.Error:</span></strong><span style="color: #0000ff;"><span style="color: #000000;"> If the request fails, return the exception wrapped in a <strong>LoadResult.Error</strong> object containing info about the exception.</span></span></li> 
</ul> 
 
 
 
<h4 class="wp-block-heading"><span style="color: #000080;"><strong class="id iz">Pager and PagingData</strong></span></h4> 
 
 
 
<p>The container for the data returned from PagingSource is called <span style="color: #008000;"><strong>PagingData</strong></span>. A new instance of PagingData is created every time your data is refreshed (pulled from the network).</p> 
 
 
 
<p>A <strong><span style="color: #008000;">Pager</span></strong> instance has to be created to build a PagingData stream, using a <span style="color: #008000;"><strong>PagingConfig</strong></span> configuration object and a function that tells the <strong><span style="color: #008000;">Pager</span></strong> how to get an instance of your <strong><span style="color: #008000;">PagingSource</span></strong> implementation .</p> 
 
 
 
<pre class="wp-block-preformatted">class PassengersViewModel( 
 private val api: MyApi 
) : ViewModel() { 
 val passengers = 
 Pager(config = PagingConfig(pageSize = 10), pagingSourceFactory = { 
 PassengersDataSource(api) 
 }).flow.cachedIn(viewModelScope) 
}</pre> 
 
 
 
<p>The Pager instance takes the below parameter:</p> 
 
 
 
<ul class="wp-block-list"> 
<li><span style="color: #008000;"><strong>PagingSource</strong></span> is our data source created in the name <strong>PassengersDataSource</strong>.</li> 
<li><strong><span style="color: #008000;">PagingConfig</span></strong> defines how to get data from the PagingSource like page size (the number of items loaded at once from the PagingSource).</li> 
</ul> 
<p><span style="color: #0000ff;"><strong>Pager.flow</strong></span> : will convert the stream of data into a Flow<;PagingData<;Passenger>;>;.</p> 
<p><strong><span style="color: #0000ff;">cachedIn()</span></strong> : is used to persist the data beyond configuration changes. The best place to do this in a ViewModel, using the viewModelScope.</p> 
 
 
 
 
 
<p>As we are passing a parameter inside our ViewModel, we also need to create a <span id="crayon-617a3c3f1489f470854076" class="crayon-syntax crayon-syntax-inline crayon-theme-coda-special-board crayon-theme-coda-special-board-inline crayon-font-consolas"><span class="crayon-pre crayon-code"><span class="crayon-v">ViewModelFactory</span></span></span> .</p> 
<p><strong><span style="color: #0000ff;">PassengersViewModelFactory.kt</span></strong></p> 
 
 
 
<pre class="wp-block-preformatted">class PassengersViewModelFactory( 
 private val api: MyApi 
) : ViewModelProvider.NewInstanceFactory(){ 
 
 override fun <;T : ViewModel?>; create(modelClass: Class<;T>;): T { 
 return PassengersViewModel(api) as T 
 } 
}</pre> 
 
 
 
<h4 class="wp-block-heading"><span style="color: #000080;"><strong>PagingDataAdapter</strong></span></h4> 
 
 
 
<p>To connect a RecyclerView to the PagingData, implement a <span style="color: #008000;"><strong>PagingDataAdapter</strong></span>:</p> 
 
 
 
<pre class="wp-block-preformatted">class PassengersAdapter : 
 PagingDataAdapter<;Passenger, PassengersAdapter.PassengersViewHolder>;(PassengersComparator) { 
 
 override fun onCreateViewHolder( 
 parent: ViewGroup, 
 viewType: Int 
 ): PassengersViewHolder { 
 return PassengersViewHolder( 
 ItemPassengerBinding.inflate( 
 LayoutInflater.from(parent.context), parent, false 
 ) 
 ) 
 } 
 
 override fun onBindViewHolder(holder: PassengersViewHolder, position: Int) { 
 val item = getItem(position) 
 item?.let { holder.bindPassenger(it) } 
 } 
 
 inner class PassengersViewHolder(private val binding: ItemPassengerBinding) : 
 RecyclerView.ViewHolder(binding.root) { 
 
 fun bindPassenger(item: Passenger) = with(binding) { 
 imageViewAirlinesLogo.loadImage(item.airline.get(0).logo) 
 textViewHeadquarters.text = item.airline.get(0).head_quaters 
 textViewNameWithTrips.text = "${item.name}, ${item.trips} Trips" 
 } 
 } 
 
 object PassengersComparator : DiffUtil.ItemCallback<;Passenger>;() { 
 override fun areItemsTheSame(oldItem: Passenger, newItem: Passenger): Boolean { 
 return oldItem._id == newItem._id 
 } 
 
 override fun areContentsTheSame(oldItem: Passenger, newItem: Passenger): Boolean { 
 return oldItem == newItem 
 } 
 } 
}</pre> 
 
 
 
<ul> 
<li class="hq hr dm hs b ht lm hv hw hx ln hz ia ib lo id ie if lp ih ii ij lq il im in lf lg lh ej" data-selectable-paragraph="">The <strong><span style="color: #008000;">PagingDataAdapter</span></strong> listens to internal <span style="color: #008000;"><strong>PagingData</strong></span> loading events as pages are loaded and uses <span style="color: #008000;"><strong>DiffUtil</strong></span> on a background thread to compute fine-grained updates as updated content is received in the form of new <span style="color: #008000;"><strong>PagingData</strong></span> objects.</li> 
</ul> 
<p>The <strong><span style="color: #008000;">item_passenger.xml</span></strong> defines the layout of recyclerView item.</p> 
 
 
 
<p><span style="color: #0000ff;"><strong>item_passenger.xml</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted"><;?xml version="1.0" encoding="utf-8"?>; 
<;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:orientation="vertical" 
 android:padding="12dp">; 
 
 <;ImageView 
 android:id="@+id/image_view_airlines_logo" 
 android:layout_width="280dp" 
 android:layout_height="52dp" 
 android:layout_gravity="center_horizontal" />; 
 
 <;TextView 
 android:id="@+id/text_view_headquarters" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_gravity="center_horizontal" 
 android:layout_marginTop="12dp" 
 android:textAlignment="center" 
 android:textColor="@android:color/holo_blue_dark" 
 android:textSize="14sp" 
 tools:text="Jom Phol Subdistrict, Chatuchak, Bangkok, Thailand" />; 
 
 <;TextView 
 android:id="@+id/text_view_name_with_trips" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:layout_marginTop="12dp" 
 android:gravity="center_horizontal" 
 android:textAlignment="center" 
 android:textColor="@android:color/holo_orange_dark" 
 android:textSize="22sp" 
 tools:text="Dodi Papagena, 2223 Trips" />; 
 
 <;View 
 android:layout_width="match_parent" 
 android:layout_height="1dp" 
 android:layout_marginTop="8dp" 
 android:alpha="0.3" 
 android:background="@android:color/holo_red_dark" />; 
 
<;/LinearLayout>;</pre> 
 
 
 
<p>The <span style="color: #008000;"><strong>activity_main.xml</strong></span> defines the layout of MainActivity.</p> 
<p><span style="color: #0000ff;"><strong>activity_main.xml</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted"><;?xml version="1.0" encoding="utf-8"?>; 
<;androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:app="http://schemas.android.com/apk/res-auto" 
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 tools:context=".ui.MainActivity">; 
 
 <;androidx.recyclerview.widget.RecyclerView 
 android:id="@+id/recyclerView" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 app:layout_constraintBottom_toBottomOf="parent" 
 app:layout_constraintEnd_toEndOf="parent" 
 app:layout_constraintStart_toStartOf="parent" 
 app:layout_constraintTop_toTopOf="parent" 
 tools:listitem="@layout/item_passenger" />; 
 
<;/androidx.constraintlayout.widget.ConstraintLayout>;</pre> 
 
 
 
<p>Now, in <strong><span style="color: #008000;">MainActivity.kt</span></strong> collect the <span style="color: #008000;"><strong>Flow<;PagingData>;</strong></span> and submit it to the <strong><span style="color: #008000;">PagingDataAdapter</span></strong>.</p> 
<p><strong><span style="color: #0000ff;">MainActivity.kt</span></strong></p> 
 
 
 
<pre class="wp-block-preformatted">class MainActivity : AppCompatActivity() { 
 
 lateinit var passengersViewModel: PassengersViewModel 
 lateinit var passengersAdapter: PassengersAdapter 
 private lateinit var binding: ActivityMainBinding 
 
 override fun onCreate(savedInstanceState: Bundle?) { 
 super.onCreate(savedInstanceState) 
 binding = ActivityMainBinding.inflate(layoutInflater) 
 
 setContentView(binding.root) 
 
 setupViewModel() 
 setupView() 
 setupList() 
 } 
 
 private fun setupViewModel() { 
 val factory = PassengersViewModelFactory(MyApi()) 
 passengersViewModel = ViewModelProvider(this, factory).get(PassengersViewModel::class.java) 
 } 
 
 private fun setupView() { 
 passengersAdapter = PassengersAdapter() 
 binding.recyclerView.apply { 
 layoutManager = LinearLayoutManager(context) 
 adapter = passengersAdapter 
 setHasFixedSize(true) 
 } 
 } 
 
 private fun setupList() { 
 lifecycleScope.launch { 
 passengersViewModel.passengers.collectLatest { pagedData ->; 
 passengersAdapter.submitData(pagedData) 
 } 
 } 
 } 
}</pre> 
 
 
 
<p>When you run the app it will look like this:</p> 
<p><img class="alignnone wp-image-2911" src="https://c1ctech.com/wp-content/uploads/2021/10/Screenshot_20211026-152739_JetpackPagingExp1-498x1024.jpg" alt="" width="322" height="662" /></p> 
<h4 class="wp-block-heading"><span style="color: #000080;"><strong><span id="Displaying-LoadingError-State">Displaying Loading/Error State</span></strong></span></h4> 
 
 
 
<p>The <span style="color: #008000;"><strong>item_loading_state.xml</strong></span>, defines a layout for displaying Loading/Error state.</p> 
<p><span style="color: #0000ff;"><strong>item_loading_state.xml</strong></span></p> 
 
 
 
<pre class="wp-block-preformatted"><;?xml version="1.0" encoding="utf-8"?>; 
<;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:orientation="vertical" 
 android:padding="12dp">; 
 
 <;ProgressBar 
 android:id="@+id/progressbar" 
 android:layout_width="32dp" 
 android:layout_height="32dp" 
 android:layout_gravity="center_horizontal" />; 
 
 <;TextView 
 android:id="@+id/text_view_error" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:gravity="center_horizontal" 
 android:textAlignment="center" 
 android:textColor="@android:color/holo_red_dark" 
 android:textSize="14sp" 
 android:visibility="gone" 
 tools:text="Some Error Occurred" 
 tools:visibility="visible" />; 
 
 <;TextView 
 android:id="@+id/button_retry" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_gravity="center_horizontal" 
 android:layout_marginTop="12dp" 
 android:text="Tap to Retry" 
 android:textAllCaps="false" 
 android:textColor="@android:color/holo_green_dark" 
 android:textSize="16sp" />; 
 
<;/LinearLayout>;</pre> 
 
 
 
<h4><span style="color: #000080;"><strong>LoadStateAdapter</strong></span></h4> 
 
 
 
<p>The class <strong><span style="color: #008000;">PassengersLoadStateAdapter</span></strong> extends LoadStateAdapter class.</p> 
 
 
 
<p><strong><span style="color: #0000ff;">PassengersLoadStateAdapter.kt</span></strong></p> 
 
 
 
<pre class="wp-block-preformatted">class PassengersLoadStateAdapter( 
 private val retry: () ->; Unit 
) : LoadStateAdapter<;PassengersLoadStateAdapter.PassengerLoadStateViewHolder>;() { 
 
 inner class PassengerLoadStateViewHolder( 
 private val binding: ItemLoadingStateBinding, 
 private val retry: () ->; Unit 
 ) : RecyclerView.ViewHolder(binding.root) { 
 fun bind(loadState: LoadState) { 
 if (loadState is LoadState.Error) { 
 binding.textViewError.text = loadState.error.localizedMessage 
 } 
 
 binding.progressbar.visible(loadState is LoadState.Loading) 
 binding.buttonRetry.visible(loadState is LoadState.Error) 
 binding.textViewError.visible(loadState is LoadState.Error) 
 binding.buttonRetry.setOnClickListener { 
 retry() 
 } 
 
 binding.progressbar.visibility = View.VISIBLE 
 } 
 } 
 
 override fun onBindViewHolder(holder: PassengerLoadStateViewHolder, loadState: LoadState) { 
 holder.bind(loadState) 
 } 
 
 override fun onCreateViewHolder( 
 parent: ViewGroup, 
 loadState: LoadState 
 ) = PassengerLoadStateViewHolder( 
 ItemLoadingStateBinding.inflate(LayoutInflater.from(parent.context), parent, false), 
 retry 
 ) 
}</pre> 
 
 
 
<h4><strong><span style="color: #000080;">Using LoadStateAdapter</span></strong></h4> 
<p>Now to add the LoadStateAdapter to our passengersAdapter, we will use the function <strong><span style="color: #008000;">withLoadStateHeaderAndFooter</span></strong>.</p> 
 
 
 
<pre class="wp-block-preformatted">binding.recyclerView.adapter = passengersAdapter.withLoadStateHeaderAndFooter( 
 header = PassengersLoadStateAdapter { passengersAdapter.retry() }, 
 footer = PassengersLoadStateAdapter { passengersAdapter.retry() } 
 )</pre> 
 
 
 
<p>Run your app and you will see the loading state or error state if occurred, you will also get a retry button (when network is off).</p> 
<p><img class="alignnone wp-image-2908" src="https://c1ctech.com/wp-content/uploads/2021/10/Screenshot_20211028-152635_JetpackPagingExp1-498x1024.jpg" alt="" width="322" height="663" /> <img class="alignnone wp-image-2909" src="https://c1ctech.com/wp-content/uploads/2021/10/Screenshot_20211028-152708_JetpackPagingExp1-498x1024.jpg" alt="" width="321" height="660" /></p> 
<p> ;</p> 


