# ContentPager

Artifact: [androidx.contentpager:contentpager](https://developer.android.com/jetpack/androidx/releases/contentpager) [View Source](https://cs.android.com/search?q=file:androidx/contentpager/content/ContentPager.java+class:androidx.contentpager.content.ContentPager) Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

*** ** * ** ***


```
public class ContentPager
```

<br />

*** ** * ** ***

`https://developer.android.com/reference/androidx/contentpager/content/ContentPager` provides support for loading "paged" data on a background thread using the `https://developer.android.com/reference/android/content/ContentResolver.html` framework. This provides an effective compatibility layer for the ContentResolver "paging" support added in Android O. Those Android O changes, like this class, help reduce or eliminate the occurrence of expensive inter-process shared memory operations (aka "CursorWindow swaps") happening on the UI thread when working with remote providers.

The list of terms used in this document:

- "The provider" is a `https://developer.android.com/reference/android/content/ContentProvider.html` supplying data identified by a specific content `https://developer.android.com/reference/android/net/Uri.html`. A provider is the source of data, and for the sake of this documents, the provider resides in a remote process.
- "supports paging" A provider supports paging when it returns a pre-paged `https://developer.android.com/reference/android/database/Cursor.html` that honors the paging contract. See @link ContentResolver#QUERY_ARG_OFFSET} and `https://developer.android.com/reference/android/content/ContentResolver.html#QUERY_ARG_LIMIT` for details on the contract.
- "CursorWindow swaps" The process by which new data is loaded into a shared memory via a CursorWindow instance. This is a prominent contributor to UI jank in applications that use Cursor as backing data for UI elements like `RecyclerView`.

**Details**

Data will be loaded from a content uri in one of two ways, depending on the runtime environment and if the provider supports paging.

- If the system is Android O and greater and the provider supports paging, the Cursor will be returned, effectively unmodified, to a `https://developer.android.com/reference/androidx/contentpager/content/ContentPager.ContentCallback` supplied by your application.
- If the system is less than Android O or the provider does not support paging, the loader will fetch an unpaged Cursor from the provider. The unpaged Cursor will be held by the ContentPager, and data will be copied into a new cursor in a background thread. The new cursor will be returned to a `https://developer.android.com/reference/androidx/contentpager/content/ContentPager.ContentCallback` supplied by your application.

In either cases, when an application employs this library it can generally assume that there will be no CursorWindow swap. But picking the right limit for records can help reduce or even eliminate some heavy lifting done to guard against swaps.

How do we avoid that entirely?

**Picking a reasonable item limit**

Authors are encouraged to experiment with limits using real data and the widest column projection they'll use in their app. The total number of records that will fit into shared memory varies depending on multiple factors.

- The number of columns being requested in the cursor projection. Limit the number of columns, to reduce the size of each row.
- The size of the data in each column.
- the Cursor type.

If the cursor is running in-process, there may be no need for paging. Depending on the Cursor implementation chosen there may be no shared memory/CursorWindow in use. NOTE: If the provider is running in your process, you should implement paging support inorder to make your app run fast and to consume the fewest resources possible.

In common cases where there is a low volume (in the hundreds) of records in the dataset being queried, all of the data should easily fit in shared memory. A debugger can be handy to understand with greater accuracy how many results can fit in shared memory. Inspect the Cursor object returned from a call to `https://developer.android.com/reference/android/content/ContentResolver.html#query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String)`. If the underlying type is a `https://developer.android.com/reference/android/database/CrossProcessCursor.html` or `https://developer.android.com/reference/android/database/AbstractWindowedCursor.html` it'll have a `https://developer.android.com/reference/android/database/CursorWindow.html` field. Check `https://developer.android.com/reference/android/database/CursorWindow.html#getNumRows()`. If getNumRows returns less than `https://developer.android.com/reference/android/database/Cursor.html#getCount()`, then you've found something close to the max rows that'll fit in a page. If the data in row is expected to be relatively stable in size, reduce row count by 15-20% to get a reasonable max page size.

**What if the limit I guessed was wrong?**

The library includes safeguards that protect against situations where an author specifies a record limit that exceeds the number of rows accessible without a CursorWindow swap. In such a circumstance, the Cursor will be adapted to report a count ({Cursor#getCount}) that reflects only records available without CursorWindow swap. But this involves extra work that can be eliminated with a correct limit.

In addition to adjusted coujnt, `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#EXTRA_SUGGESTED_LIMIT()` will be included in cursor extras. When EXTRA_SUGGESTED_LIMIT is present in extras, the client should strongly consider using this value as the limit for subsequent queries as doing so should help avoid the ned to wrap pre-paged cursors.

**Lifecycle and cleanup**

Cursors resulting from queries are owned by the requesting client. So they must be closed by the client at the appropriate time.

However, the library retains an internal cache of content that needs to be cleaned up. In order to cleanup, call `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#reset()`.

**Projections**

Note that projection is ignored when determining the identity of a query. When adding or removing projection, clients should call `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#reset()` to clear cached data.

## Summary

| ### Nested types |
|---|
| `public interface https://developer.android.com/reference/androidx/contentpager/content/ContentPager.ContentCallback` Callback by which a client receives results of a query. |
| `@https://developer.android.com/reference/androidx/annotation/IntDef(value = [https://developer.android.com/reference/androidx/contentpager/content/ContentPager.CURSOR_DISPOSITION_COPIED, https://developer.android.com/reference/androidx/contentpager/content/ContentPager.CURSOR_DISPOSITION_PAGED, https://developer.android.com/reference/androidx/contentpager/content/ContentPager.CURSOR_DISPOSITION_REPAGED, https://developer.android.com/reference/androidx/contentpager/content/ContentPager.CURSOR_DISPOSITION_WRAPPED]) @https://developer.android.com/reference/java/lang/annotation/Retention.html(value = https://developer.android.com/reference/java/lang/annotation/RetentionPolicy.html#SOURCE) public annotation https://developer.android.com/reference/androidx/contentpager/content/ContentPager.CursorDisposition` |
| `public interface https://developer.android.com/reference/androidx/contentpager/content/ContentPager.QueryRunner` Implementations of this interface provide the mechanism for execution of queries off the UI thread. |
| `public interface https://developer.android.com/reference/androidx/contentpager/content/ContentPager.QueryRunner.Callback` Callback that receives a cursor once a query as been executed on the Runner. |

| ### Constants |
|---|---|
| `static final int` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#CURSOR_DISPOSITION_COPIED() = 1` The cursor size exceeded page size. |
| `static final int` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#CURSOR_DISPOSITION_PAGED() = 2` The cursor was provider paged. |
| `static final int` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#CURSOR_DISPOSITION_REPAGED() = 3` The cursor was pre-paged, but total size was larger than CursorWindow size. |
| `static final int` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#CURSOR_DISPOSITION_WRAPPED() = 4` The cursor was not pre-paged, but total size was smaller than page size. |
| `static final https://developer.android.com/reference/java/lang/String.html` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#EXTRA_HONORED_ARGS() = "android.content.extra.HONORED_ARGS"` |
| `static final https://developer.android.com/reference/java/lang/String.html` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#EXTRA_REQUESTED_LIMIT() = "android-support:extra-ignored-limit"` Denotes the requested limit, if the limit was not-honored. |
| `static final https://developer.android.com/reference/java/lang/String.html` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#EXTRA_SUGGESTED_LIMIT() = "android-support:extra-suggested-limit"` Specifies a limit likely to fit in CursorWindow limit. |
| `static final https://developer.android.com/reference/java/lang/String.html` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#EXTRA_TOTAL_COUNT() = "android.content.extra.TOTAL_COUNT"` |
| `static final https://developer.android.com/reference/java/lang/String.html` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#QUERY_ARG_LIMIT() = "android:query-arg-limit"` |
| `static final https://developer.android.com/reference/java/lang/String.html` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#QUERY_ARG_OFFSET() = "android:query-arg-offset"` |

| ### Public constructors |
|---|
| `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#ContentPager(android.content.ContentResolver,androidx.contentpager.content.ContentPager.QueryRunner)( https://developer.android.com/reference/android/content/ContentResolver.html resolver, https://developer.android.com/reference/androidx/contentpager/content/ContentPager.QueryRunner queryRunner )` Creates a new ContentPager with a default cursor cache size of 1. |
| `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#ContentPager(android.content.ContentResolver,androidx.contentpager.content.ContentPager.QueryRunner,int)( @https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/android/content/ContentResolver.html resolver, @https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/androidx/contentpager/content/ContentPager.QueryRunner queryRunner, int cursorCacheSize )` Creates a new ContentPager. |

| ### Public methods |
|---|---|
| `static @https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/android/os/Bundle.html` | `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#createArgs(int,int)(int offset, int limit)` Builds a Bundle with offset and limit values suitable for with `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#query(android.net.Uri,java.lang.String[],android.os.Bundle,android.os.CancellationSignal,androidx.contentpager.content.ContentPager.ContentCallback)`. |
| `@https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/androidx/contentpager/content/Query` | `@https://developer.android.com/reference/androidx/annotation/MainThread https://developer.android.com/reference/androidx/contentpager/content/ContentPager#query(android.net.Uri,java.lang.String[],android.os.Bundle,android.os.CancellationSignal,androidx.contentpager.content.ContentPager.ContentCallback)( @https://developer.android.com/reference/androidx/annotation/RequiresPermission.Read @https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/android/net/Uri.html uri, @https://developer.android.com/reference/androidx/annotation/Nullable String[] projection, @https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/android/os/Bundle.html queryArgs, @https://developer.android.com/reference/androidx/annotation/Nullable https://developer.android.com/reference/android/os/CancellationSignal.html cancellationSignal, @https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/androidx/contentpager/content/ContentPager.ContentCallback callback )` Initiates loading of content. |
| `void` | `@https://developer.android.com/reference/androidx/annotation/MainThread https://developer.android.com/reference/androidx/contentpager/content/ContentPager#reset()()` Clears any cached data. |

## Constants

### CURSOR_DISPOSITION_COPIED

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final int CURSOR_DISPOSITION_COPIED = 1
```

The cursor size exceeded page size. A new cursor with with page data was created.

### CURSOR_DISPOSITION_PAGED

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final int CURSOR_DISPOSITION_PAGED = 2
```

The cursor was provider paged.

### CURSOR_DISPOSITION_REPAGED

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final int CURSOR_DISPOSITION_REPAGED = 3
```

The cursor was pre-paged, but total size was larger than CursorWindow size.

### CURSOR_DISPOSITION_WRAPPED

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final int CURSOR_DISPOSITION_WRAPPED = 4
```

The cursor was not pre-paged, but total size was smaller than page size. Cursor wrapped to supply data in extras only.

### EXTRA_HONORED_ARGS

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS"
```

| See also |
|---|---|
| `https://developer.android.com/reference/android/content/ContentResolver.html#EXTRA_HONORED_ARGS` |   |

### EXTRA_REQUESTED_LIMIT

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final String EXTRA_REQUESTED_LIMIT = "android-support:extra-ignored-limit"
```

Denotes the requested limit, if the limit was not-honored.

### EXTRA_SUGGESTED_LIMIT

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final String EXTRA_SUGGESTED_LIMIT = "android-support:extra-suggested-limit"
```

Specifies a limit likely to fit in CursorWindow limit.

### EXTRA_TOTAL_COUNT

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final String EXTRA_TOTAL_COUNT = "android.content.extra.TOTAL_COUNT"
```

| See also |
|---|---|
| `https://developer.android.com/reference/android/content/ContentResolver.html#EXTRA_TOTAL_COUNT` |   |

### QUERY_ARG_LIMIT

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final String QUERY_ARG_LIMIT = "android:query-arg-limit"
```

| See also |
|---|---|
| `https://developer.android.com/reference/android/content/ContentResolver.html#QUERY_ARG_LIMIT` |   |

### QUERY_ARG_OFFSET

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static final String QUERY_ARG_OFFSET = "android:query-arg-offset"
```

| See also |
|---|---|
| `https://developer.android.com/reference/android/content/ContentResolver.html#QUERY_ARG_OFFSET` |   |

## Public constructors

### ContentPager

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public ContentPager(
    ContentResolver resolver,
    ContentPager.QueryRunner queryRunner
)
```

Creates a new ContentPager with a default cursor cache size of 1.

### ContentPager

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public ContentPager(
    @NonNull ContentResolver resolver,
    @NonNull ContentPager.QueryRunner queryRunner,
    int cursorCacheSize
)
```

Creates a new ContentPager.

| Parameters |
|---|---|
| `@https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/android/content/ContentResolver.html resolver` | The content resolver to use when performing queries. |
| `@https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/androidx/contentpager/content/ContentPager.QueryRunner queryRunner` | The query running to use. This provides a means of executing queries on a background thread. |
| `int cursorCacheSize` | Specifies the size of the unpaged cursor cache. If you will only be querying a single content Uri, 1 is sufficient. If you wish to use a single ContentPager for queries against several independent Uris this number should be increased to reflect that. Remember that adding or modifying a query argument creates a new Uri. |

## Public methods

### createArgs

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
public static @NonNull Bundle createArgs(int offset, int limit)
```

Builds a Bundle with offset and limit values suitable for with `https://developer.android.com/reference/androidx/contentpager/content/ContentPager#query(android.net.Uri,java.lang.String[],android.os.Bundle,android.os.CancellationSignal,androidx.contentpager.content.ContentPager.ContentCallback)`.

| Parameters |
|---|---|
| `int offset` | must be greater than or equal to 0. |
| `int limit` | can be any value. Only values greater than or equal to 0 are respected. If any other value results in no upper limit on results. Note that a well behaved client should probably supply a reasonable limit. See class documentation on how to select a limit. |

| Returns |
|---|---|
| `@https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/android/os/Bundle.html` | Mutable Bundle pre-populated with offset and limits vales. |

### query

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
@MainThread
public @NonNull Query query(
    @RequiresPermission.Read @NonNull Uri uri,
    @Nullable String[] projection,
    @NonNull Bundle queryArgs,
    @Nullable CancellationSignal cancellationSignal,
    @NonNull ContentPager.ContentCallback callback
)
```

Initiates loading of content. For details on all params but callback, see `https://developer.android.com/reference/android/content/ContentResolver.html#query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal)`.

| Parameters |
|---|---|
| `@https://developer.android.com/reference/androidx/annotation/RequiresPermission.Read @https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/android/net/Uri.html uri` | The URI, using the content:// scheme, for the content to retrieve. |
| `@https://developer.android.com/reference/androidx/annotation/Nullable String[] projection` | A list of which columns to return. Passing null will return the default project as determined by the provider. This can be inefficient, so it is best to supply a projection. |
| `@https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/android/os/Bundle.html queryArgs` | A Bundle containing any arguments to the query. |
| `@https://developer.android.com/reference/androidx/annotation/Nullable https://developer.android.com/reference/android/os/CancellationSignal.html cancellationSignal` | A signal to cancel the operation in progress, or null if none. If the operation is canceled, then `https://developer.android.com/reference/android/os/OperationCanceledException.html` will be thrown when the query is executed. |
| `@https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/androidx/contentpager/content/ContentPager.ContentCallback callback` | The callback that will receive the query results. |

| Returns |
|---|---|
| `@https://developer.android.com/reference/androidx/annotation/NonNull https://developer.android.com/reference/androidx/contentpager/content/Query` | A Query object describing the query. |

### reset

Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/contentpager#1.0.0)

```
@MainThread
public void reset()
```

Clears any cached data. This method must be called in order to cleanup runtime state (like cursors).