The Paging library provides powerful capabilities for loading and displaying
paged data from a larger dataset. This guide demonstrates how to use the Paging
library to set up a stream of paged data from a network data source and display
it in a [`RecyclerView`](https://developer.android.com/guide/topics/ui/layout/recyclerview).

## Define a data source

The first step is to define a
[`PagingSource`](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource) implementation
to identify the data source. The `PagingSource` API class includes the
[`load()`](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource#load)
method, which you override to indicate how to retrieve paged data from the
corresponding data source.

Use the `PagingSource` class directly to use Kotlin coroutines for async
loading. The Paging library also provides classes to support other async
frameworks:

- To use RxJava, implement [`RxPagingSource`](https://developer.android.com/reference/kotlin/androidx/paging/rxjava2/RxPagingSource) instead.
- To use `ListenableFuture` from Guava, implement [`ListenableFuturePagingSource`](https://developer.android.com/reference/kotlin/androidx/paging/ListenableFuturePagingSource) instead.

| **Note:** To use the Paging library with RxJava or Guava, you must include [additional
| dependencies](https://developer.android.com/topic/libraries/architecture/paging/v3-overview#setup).

### Select key and value types

`PagingSource<Key, Value>` has two type parameters: `Key` and `Value`. The key
defines the identifier used to load the data, and the value is the type of the
data itself. For example, if you load pages of `User` objects from the network
by passing `Int` page numbers to
[Retrofit](https://square.github.io/retrofit/),
select `Int` as the `Key` type and `User` as the `Value` type.

### Define the PagingSource

The following example implements a
[`PagingSource`](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource) that loads
pages of items by page number. The `Key` type is `Int` and the `Value` type is
`User`.  

### Kotlin

```kotlin
class ExamplePagingSource(
    val backend: ExampleBackendService,
    val query: String
) : PagingSource<Int, User>() {
  override suspend fun load(
    params: LoadParams<Int>
  ): LoadResult<Int, User> {
    try {
      // Start refresh at page 1 if undefined.
      val nextPageNumber = params.key ?: 1
      val response = backend.searchUsers(query, nextPageNumber)
      return LoadResult.Page(
        data = response.users,
        prevKey = null, // Only paging forward.
        nextKey = response.nextPageNumber
      )
    } catch (e: Exception) {
      // Handle errors in this block and return LoadResult.Error for
      // expected errors (such as a network failure).
    }
  }

  override fun getRefreshKey(state: PagingState<Int, User>): Int? {
    // Try to find the page key of the closest page to anchorPosition from
    // either the prevKey or the nextKey; you need to handle nullability
    // here.
    //  * prevKey == null -> anchorPage is the first page.
    //  * nextKey == null -> anchorPage is the last page.
    //  * both prevKey and nextKey are null -> anchorPage is the
    //    initial page, so return null.
    return state.anchorPosition?.let { anchorPosition ->
      val anchorPage = state.closestPageToPosition(anchorPosition)
      anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
    }
  }
}
```

### Java

```java
class ExamplePagingSource extends RxPagingSource<Integer, User> {
  @NonNull
  private ExampleBackendService mBackend;
  @NonNull
  private String mQuery;

  ExamplePagingSource(@NonNull ExampleBackendService backend,
    @NonNull String query) {
    mBackend = backend;
    mQuery = query;
  }

  @NotNull
  @Override
  public Single<LoadResult<Integer, User>> loadSingle(
    @NotNull LoadParams<Integer> params) {
    // Start refresh at page 1 if undefined.
    Integer nextPageNumber = params.getKey();
    if (nextPageNumber == null) {
      nextPageNumber = 1;
    }

    return mBackend.searchUsers(mQuery, nextPageNumber)
      .subscribeOn(Schedulers.io())
      .map(this::toLoadResult)
      .onErrorReturn(LoadResult.Error::new);
  }

  private LoadResult<Integer, User> toLoadResult(
    @NonNull SearchUserResponse response) {
    return new LoadResult.Page<>(
      response.getUsers(),
      null, // Only paging forward.
      response.getNextPageNumber(),
      LoadResult.Page.COUNT_UNDEFINED,
      LoadResult.Page.COUNT_UNDEFINED);
  }

  @Nullable
  @Override
  public Integer getRefreshKey(@NotNull PagingState<Integer, User> state) {
    // Try to find the page key of the closest page to anchorPosition from
    // either the prevKey or the nextKey; you need to handle nullability
    // here.
    //  * prevKey == null -> anchorPage is the first page.
    //  * nextKey == null -> anchorPage is the last page.
    //  * both prevKey and nextKey are null -> anchorPage is the
    //    initial page, so return null.
    Integer anchorPosition = state.getAnchorPosition();
    if (anchorPosition == null) {
      return null;
    }

    LoadResult.Page<Integer, User> anchorPage = state.closestPageToPosition(anchorPosition);
    if (anchorPage == null) {
      return null;
    }

    Integer prevKey = anchorPage.getPrevKey();
    if (prevKey != null) {
      return prevKey + 1;
    }

    Integer nextKey = anchorPage.getNextKey();
    if (nextKey != null) {
      return nextKey - 1;
    }

    return null;
  }
}
```

### Java

```java
class ExamplePagingSource extends ListenableFuturePagingSource<Integer, User> {
  @NonNull
  private ExampleBackendService mBackend;
  @NonNull
  private String mQuery;
  @NonNull
  private Executor mBgExecutor;

  ExamplePagingSource(
    @NonNull ExampleBackendService backend,
    @NonNull String query, @NonNull Executor bgExecutor) {
    mBackend = backend;
    mQuery = query;
    mBgExecutor = bgExecutor;
  }

  @NotNull
  @Override
  public ListenableFuture<LoadResult<Integer, User>> loadFuture(@NotNull LoadParams<Integer> params) {
    // Start refresh at page 1 if undefined.
    Integer nextPageNumber = params.getKey();
    if (nextPageNumber == null) {
      nextPageNumber = 1;
    }

    ListenableFuture<LoadResult<Integer, User>> pageFuture =
      Futures.transform(mBackend.searchUsers(mQuery, nextPageNumber),
      this::toLoadResult, mBgExecutor);

    ListenableFuture<LoadResult<Integer, User>> partialLoadResultFuture =
      Futures.catching(pageFuture, HttpException.class,
      LoadResult.Error::new, mBgExecutor);

    return Futures.catching(partialLoadResultFuture,
      IOException.class, LoadResult.Error::new, mBgExecutor);
  }

  private LoadResult<Integer, User> toLoadResult(@NonNull SearchUserResponse response) {
    return new LoadResult.Page<>(response.getUsers(),
    null, // Only paging forward.
    response.getNextPageNumber(),
    LoadResult.Page.COUNT_UNDEFINED,
    LoadResult.Page.COUNT_UNDEFINED);
  }

  @Nullable
  @Override
  public Integer getRefreshKey(@NotNull PagingState<Integer, User> state) {
    // Try to find the page key of the closest page to anchorPosition from
    // either the prevKey or the nextKey; you need to handle nullability
    // here.
    //  * prevKey == null -> anchorPage is the first page.
    //  * nextKey == null -> anchorPage is the last page.
    //  * both prevKey and nextKey are null -> anchorPage is the
    //    initial page, so return null.
    Integer anchorPosition = state.getAnchorPosition();
    if (anchorPosition == null) {
      return null;
    }

    LoadResult.Page<Integer, User> anchorPage = state.closestPageToPosition(anchorPosition);
    if (anchorPage == null) {
      return null;
    }

    Integer prevKey = anchorPage.getPrevKey();
    if (prevKey != null) {
      return prevKey + 1;
    }

    Integer nextKey = anchorPage.getNextKey();
    if (nextKey != null) {
      return nextKey - 1;
    }

    return null;
  }
}
```

A typical `PagingSource` implementation passes parameters provided in its
constructor to the `load()` method to load appropriate data for a query. In the
example above, those parameters are:

- `backend`: an instance of the backend service that provides the data
- `query`: the search query to send to the service indicated by `backend`

The [`LoadParams`](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource.LoadParams)
object contains information about the load operation to be performed. This
includes the key to be loaded and the number of items to be loaded.

The [`LoadResult`](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource.LoadResult)
object contains the result of the load operation. `LoadResult` is a sealed class
that takes one of two forms, depending on whether the `load()` call succeeded:

- If the load is successful, return a `LoadResult.Page` object.
- If the load is not successful, return a `LoadResult.Error` object.

The following figure illustrates how the `load()` function in this example
receives the key for each load and provides the key for the subsequent load.
![On each load() call, the ExamplePagingSource takes in the current key
and returns the next key to load.](https://developer.android.com/static/topic/libraries/architecture/images/paging3-source-load.svg) **Figure 1.** Diagram showing how `load()` uses and updates the key.

The `PagingSource` implementation must also implement a
[`getRefreshKey()`](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource#getrefreshkey)
method that takes a
[`PagingState`](https://developer.android.com/reference/kotlin/androidx/paging/PagingState) object as a
parameter. It returns the key to pass into the `load()` method when the data is
refreshed or invalidated after the initial load. The Paging Library calls this
method automatically on subsequent refreshes of the data.

### Handle errors

Requests to load data can fail for a number of reasons, especially when loading
over a network. Report errors encountered during loading by returning a
`LoadResult.Error` object from the `load()` method.

For example, you can catch and report loading errors in `ExamplePagingSource`
from the previous example by adding the following to the `load()` method:  

### Kotlin

```kotlin
catch (e: IOException) {
  // IOException for network failures.
  return LoadResult.Error(e)
} catch (e: HttpException) {
  // HttpException for any non-2xx HTTP status codes.
  return LoadResult.Error(e)
}
```

### Java

```java
return backend.searchUsers(searchTerm, nextPageNumber)
  .subscribeOn(Schedulers.io())
  .map(this::toLoadResult)
  .onErrorReturn(LoadResult.Error::new);
```

### Java

```java
ListenableFuture<LoadResult<Integer, User>> pageFuture = Futures.transform(
  backend.searchUsers(query, nextPageNumber), this::toLoadResult,
  bgExecutor);

ListenableFuture<LoadResult<Integer, User>> partialLoadResultFuture = Futures.catching(
  pageFuture, HttpException.class, LoadResult.Error::new,
  bgExecutor);

return Futures.catching(partialLoadResultFuture,
  IOException.class, LoadResult.Error::new, bgExecutor);
```

For more information on handling Retrofit errors, see the samples in the
`PagingSource` API reference.

`PagingSource` collects and delivers `LoadResult.Error` objects to the UI so
that you can act on them. For more information on exposing the loading state
in the UI, see [Manage and present loading
states](https://developer.android.com/topic/libraries/architecture/paging/load-state).

## Set up a stream of PagingData

Next, you need a stream of paged data from the `PagingSource` implementation.
Set up the data stream in your `ViewModel`. The
[`Pager`](https://developer.android.com/reference/kotlin/androidx/paging/Pager) class provides methods that
expose a reactive stream of
[`PagingData`](https://developer.android.com/reference/kotlin/androidx/paging/PagingData) objects from a
`PagingSource`. The Paging library supports using several stream types,
including `Flow`, `LiveData`, and the `Flowable` and `Observable` types from
RxJava.

When you create a `Pager` instance to set up your reactive stream, you must
provide the instance with a
[`PagingConfig`](https://developer.android.com/reference/kotlin/androidx/paging/PagingConfig) configuration
object and a function that tells `Pager` how to get an instance of your
`PagingSource` implementation:  

### Kotlin

```kotlin
val flow = Pager(
  // Configure how data is loaded by passing additional properties to
  // PagingConfig, such as prefetchDistance.
  PagingConfig(pageSize = 20)
) {
  ExamplePagingSource(backend, query)
}.flow
  .cachedIn(viewModelScope)
```

### Java

```java
// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
Pager<Integer, User> pager = Pager<>(
  new PagingConfig(/* pageSize = */ 20),
  () -> ExamplePagingSource(backend, query));

Flowable<PagingData<User>> flowable = PagingRx.getFlowable(pager);
PagingRx.cachedIn(flowable, viewModelScope);
```

### Java

```java
// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
Pager<Integer, User> pager = Pager<>(
  new PagingConfig(/* pageSize = */ 20),
  () -> ExamplePagingSource(backend, query));

PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope);
```

The `cachedIn()` operator makes the data stream shareable and caches the loaded
data with the provided `CoroutineScope`. This example uses the `viewModelScope`
provided by the lifecycle `lifecycle-viewmodel-ktx` artifact.

The `Pager` object calls the `load()` method from the `PagingSource` object,
providing it with the
[`LoadParams`](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource.LoadParams) object
and receiving the
[`LoadResult`](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource.LoadResult) object
in return.

## Define a RecyclerView adapter

You also need to set up an adapter to receive the data into your `RecyclerView`
list. The Paging library provides the `PagingDataAdapter` class for this
purpose.

Define a class that extends `PagingDataAdapter`. In the example,
`UserAdapter` extends `PagingDataAdapter` to provide a `RecyclerView`
adapter for list items of type `User` and using `UserViewHolder` as a [view
holder](https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/RecyclerView.ViewHolder):  

### Kotlin

```kotlin
class UserAdapter(diffCallback: DiffUtil.ItemCallback<User>) :
  PagingDataAdapter<User, UserViewHolder>(diffCallback) {
  override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
  ): UserViewHolder {
    return UserViewHolder(parent)
  }

  override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
    val item = getItem(position)
    // Note that item can be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item)
  }
}
```

### Java

```java
class UserAdapter extends PagingDataAdapter<User, UserViewHolder> {
  UserAdapter(@NotNull DiffUtil.ItemCallback<User> diffCallback) {
    super(diffCallback);
  }

  @NonNull
  @Override
  public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new UserViewHolder(parent);
  }

  @Override
  public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
    User item = getItem(position);
    // Note that item can be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item);
  }
}
```

### Java

```java
class UserAdapter extends PagingDataAdapter<User, UserViewHolder> {
  UserAdapter(@NotNull DiffUtil.ItemCallback<User> diffCallback) {
    super(diffCallback);
  }

  @NonNull
  @Override
  public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new UserViewHolder(parent);
  }

  @Override
  public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
    User item = getItem(position);
    // Note that item can be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item);
  }
}
```

Your adapter must also define the `onCreateViewHolder()` and
`onBindViewHolder()` methods and specify a
[`DiffUtil.ItemCallback`](https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/DiffUtil.ItemCallback).
This works the same as it normally does when defining `RecyclerView` list
adapters:  

### Kotlin

```kotlin
object UserComparator : DiffUtil.ItemCallback<User>() {
  override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
    // Id is unique.
    return oldItem.id == newItem.id
  }

  override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
    return oldItem == newItem
  }
}
```

### Java

```java
class UserComparator extends DiffUtil.ItemCallback<User> {
  @Override
  public boolean areItemsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    // Id is unique.
    return oldItem.id.equals(newItem.id);
  }

  @Override
  public boolean areContentsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    return oldItem.equals(newItem);
  }
}
```

### Java

```java
class UserComparator extends DiffUtil.ItemCallback<User> {
  @Override
  public boolean areItemsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    // Id is unique.
    return oldItem.id.equals(newItem.id);
  }

  @Override
  public boolean areContentsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    return oldItem.equals(newItem);
  }
}
```

## Display the paged data in your UI

Now that you have defined a `PagingSource`, created a way for your app to
generate a stream of `PagingData`, and defined a `PagingDataAdapter`, you are
ready to connect these elements together and display paged data in your
activity.

Perform the following steps in your activity's `onCreate` or fragment's
`onViewCreated` method:

1. Create an instance of your `PagingDataAdapter` class.
2. Pass the `PagingDataAdapter` instance to the [`RecyclerView`](https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/RecyclerView) list that you want to display your paged data.
3. Observe the `PagingData` stream and pass each generated value to your adapter's `submitData()` method.

### Kotlin

```kotlin
val viewModel by viewModels<ExampleViewModel>()

val pagingAdapter = UserAdapter(UserComparator)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.adapter = pagingAdapter

// Activities can use lifecycleScope directly; fragments use
// viewLifecycleOwner.lifecycleScope.
lifecycleScope.launch {
  viewModel.flow.collectLatest { pagingData ->
    pagingAdapter.submitData(pagingData)
  }
}
```

### Java

```java
ExampleViewModel viewModel = new ViewModelProvider(this)
  .get(ExampleViewModel.class);

UserAdapter pagingAdapter = new UserAdapter(new UserComparator());
RecyclerView recyclerView = findViewById<RecyclerView>(
  R.id.recycler_view);
recyclerView.adapter = pagingAdapter

viewModel.flowable
  // Using AutoDispose to handle subscription lifecycle.
  // See: https://github.com/uber/AutoDispose.
  .to(autoDisposable(AndroidLifecycleScopeProvider.from(this)))
  .subscribe(pagingData -> pagingAdapter.submitData(lifecycle, pagingData));
```

### Java

```java
ExampleViewModel viewModel = new ViewModelProvider(this)
  .get(ExampleViewModel.class);

UserAdapter pagingAdapter = new UserAdapter(new UserComparator());
RecyclerView recyclerView = findViewById<RecyclerView>(
  R.id.recycler_view);
recyclerView.adapter = pagingAdapter

// Activities can use getLifecycle() directly; fragments use
// getViewLifecycleOwner().getLifecycle().
viewModel.liveData.observe(this, pagingData ->
  pagingAdapter.submitData(getLifecycle(), pagingData));
```

The `RecyclerView` list now displays the paged data from the data source and
automatically loads another page when necessary.
| **Caution:** The `submitData()` method suspends and does not return until either the `PagingSource` is invalidated or the adapter's refresh method is called. This means that code after the `submitData()` call might execute much later than you intend.

## Additional resources

To learn more about the Paging library, see the following additional resources:

### Codelabs

- [Android Paging Advanced codelab](https://codelabs.developers.google.com/codelabs/android-paging)

## Recommended for you

- Note: link text is displayed when JavaScript is off
- [Page from network and database](https://developer.android.com/topic/libraries/architecture/paging/v3-network-db)
- [Migrate to Paging 3](https://developer.android.com/topic/libraries/architecture/paging/v3-migration)
- [Paging library overview](https://developer.android.com/topic/libraries/architecture/paging/v3-overview)