4

Introduction to Paging 3.0 in the MAD Skills Series

 2 years ago
source link: https://medium.com/androiddevelopers/introduction-to-paging-3-0-in-the-mad-skills-series-648f77231121
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Introduction to Paging 3.0 in the MAD Skills Series

Oct 11 · 5 min read

Welcome to the Paging 3.0 MAD Skills series! In this article I’ll be introducing Paging 3.0 and highlighting how to integrate it into the data layer of your application. Want to watch and read? Check out the video for this episode here!

Paging MAD Skills episode 1

Why Paging 3.0?

Showing lists of data to a user is one of the most common UI patterns. When working with large datasets, you can improve app performance and reduce memory usage by fetching/displaying data asynchronously in chunks. This is a common enough pattern that having a library provide an abstraction that facilitates this is a huge boon. These are use cases that Paging 3.0 solves with aplomb. As an added bonus, it also allows your app to support infinite datasets, and if you’re pulling down network data, it offers avenues for local caching as well.

If you’re currently using Paging 2.0, Paging 3.0 offers a bunch of improvements over its predecessor including:

  • First-class support for Kotlin coroutines and Flow.
  • Support for async loading using RxJava Single or Guava ListenableFuture primitives.
  • Built-in load state and error signals for responsive UI design, including retry and refresh functionality.
  • Improvements to the repository layer, including cancellation support and a simplified data source interface.
  • Improvements to the presentation layer, list separators, custom page transforms, and loading state headers and footers.

Be sure to check out the Paging 2.0 to Paging 3.0 migration guide for more information.

Dropping it in

In the scheme of your app’s architecture, Paging 3.0 is best suited as a means of fetching data from your data layer, and ferrying it for transformation and presentation in the UI layers via the ViewModel. Access to your data layer in Paging 3.0 is through a type called the PagingSource, a class that defines how to fetch and refresh data around boundaries defined by a PagingConfig.

A PagingSource, similar to a Map, is defined by two generic types: that of its paging key, and that of the data loaded. For example, the declaration of a PagingSource that fetched Repo items from the page based Github API would be defined as:

PagingSource definition

The PagingSource needs to implement 2 abstract methods to be fully functional:

  1. load()
  2. getRefreshKey()

The load method

The load() method, much like its name implies, is called by the Paging library to asynchronously fetch data to be displayed. This is either for the initial load, or in response to the user scrolling around. The trigger for the call to load can be ascertained from the LoadParams object passed as an argument to the method. It keeps information related to the load operation including the following:

  • Key of the page to be loaded: If this is the first time that load is called (initial load), the LoadParams.key will be null. In this case, you will have to define the initial page key.
  • Load size: the requested number of items to load.

The return type of load is a LoadResult. It can either be:

  • LoadResult.Page: for successful loads.
  • LoadResult.Error: for when errors arise.
load implementation

Note that by default, the initial load size is 3 * page size. This ensures that the first time the list is loaded the user will see enough items without triggering too many network requests if the user scrolls a bit. This is also something to consider when computing the next key in the PagingSource implementation.

The getRefreshKey method

The refresh key is used for subsequent refresh calls to PagingSource.load() (the first call is initial load which uses the initial key provided to the Pager). A refresh happens whenever the Paging library wants to load new data to replace the current list, e.g., on swipe to refresh or on invalidation due to database updates, config changes, process death, etc. Typically, subsequent refresh calls will want to restart loading data centered around PagingState.anchorPosition which represents the most recently accessed index.

getRefreshKey implementation

The Pager object

With the PagingSource defined, we can now create a Pager. A Pager is a class that is responsible for incrementally pulling chunks of data from the PagingSource as requested by the UI. Since the Pager requires access to the PagingSource, it is typically created in the data layer where the PagingSource is defined.

The second thing needed to construct a Pager is the PagingConfig which defines parameters that govern how the Pager will fetch data. It exposes quite a few optional arguments that let you fine tune the Pager’s behavior, however the pageSize argument is required.

Creating a Pager

A brief description of the arguments utilized in the construction of the PagingConfig above follows:

  • pageSize: The number of items to be loaded at once from the PagingSource.
  • enablePlaceholders: Whether the PagingData returns null for items that haven’t been loaded yet.

We typically want the pageSize to be large enough (at least enough to fill the visible area of the screen, but preferably 2–3x that) so the Pager doesn’t have to keep fetching data over and over again to show enough material on the screen as the user scrolls.

Getting your data

The type produced from the Pager is PagingData, a type that provides a distinct window into its backing PagingSource. As a user scrolls through the list, the paging data will keep fetching from the PagingSource to provide content. Should the PagingSource be invalidated, a new PagingData will be emitted to make sure the items being paginated through are in sync with what is displayed in the UI. It may help to think of a PagingData as a snapshot of the PagingSource at an instance in time.

Since a PagingSource is a snapshot that changes when the PagingSource is invalidated, the Paging Library offers multiple ways of consuming PagingData as a stream including:

  • Kotlin Flow via Pager.flow
  • LiveData via Pager.liveData
  • RxJava Flowable via Pager.flowable
  • RxJava Observable via Pager.observable

This stream of PagingData is what the ViewModel can then opt to transform before presenting the paged items to the UI.

What’s next?

With the above, we’ve integrated Paging 3.0 into the data layer of the app! Stay tuned to see how to consume PagingData in the UI and populate our list of repos!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK