Paging 3 Quiz
ARCHITECTURE › Components
Why is cachedIn(viewModelScope) typically applied to a Pager's Flow<PagingData> in the ViewModel?
- It converts the Flow into LiveData so the UI can observe it directly
- It persists the paged data to disk for offline use and later access
- It caches loaded pages and shares them so data survives rotation
- It is required to enable placeholders in PagingData for every page
Answer: It caches loaded pages and shares them so data survives rotation
cachedIn() caches the loaded PagingData in the given scope and multicasts it, so rotation or repeated collection does not re-fetch; offline persistence is a RemoteMediator/DB concern, not cachedIn.
What does returning LoadResult.Page(data, prevKey = null, nextKey = null) from a PagingSource indicate?
- There is nothing more to load in either direction
- The load failed and should be retried after a backoff
- Placeholders should be disabled for this particular page
- The PagingSource is now invalid and must be recreated
Answer: There is nothing more to load in either direction
A null prevKey/nextKey tells Paging there is nothing more to load in that direction, so it stops prepending/appending; failures use LoadResult.Error and staleness uses LoadResult.Invalid.
In a network+database setup using RemoteMediator, what acts as the single source of truth for the UI?
- The in-memory PagingData snapshot
- The network API response cache
- A separate PagingSource that calls the network directly
- The local database, which the PagingSource reads from
Answer: The local database, which the PagingSource reads from
With a RemoteMediator the database is the single source of truth: the mediator writes network results into the DB and a DB-backed PagingSource (e.g. from Room) serves the UI, enabling offline support.
Which load direction's LoadState should drive a full-screen loading spinner or initial error screen?
- append
- refresh
- prepend
- mediator only
Answer: refresh
loadState.refresh covers the initial load and manual refreshes, so it maps to the full-screen spinner/empty/error UI; append and prepend drive inline edge indicators while scrolling.
By default, what is PagingConfig.initialLoadSize relative to pageSize?
- Equal to pageSize
- Half of pageSize
- Three times pageSize
- Equal to prefetchDistance
Answer: Three times pageSize
initialLoadSize defaults to pageSize * 3 so the first load fills more of the screen and reduces immediate follow-up loads; prefetchDistance defaults to pageSize.
In Jetpack Compose, how do you correctly provide stable item identity for a paged LazyColumn?
- Pass items.itemKey { it.id } to the key parameter of items()
- Use the list index as the key, since Paging guarantees stable indices
- Wrap every row in a remember { } block
- Call collectAsState() instead of collectAsLazyPagingItems()
Answer: Pass items.itemKey { it.id } to the key parameter of items()
LazyPagingItems.itemKey { it.id } supplies stable keys that handle placeholders and shifting indices; raw indices are unstable because items shift as pages load, and collectAsState would not give a LazyPagingItems.
How should a user-triggered "Retry" button after a failed paged load be wired?
- Re-create the Pager and PagingConfig from scratch each time
- Collect the Flow again from a new coroutine scope
- Call cachedIn() again to invalidate the paging cache
- Call retry() on the LazyPagingItems or PagingDataAdapter
Answer: Call retry() on the LazyPagingItems or PagingDataAdapter
retry() re-attempts only the failed load(s) without resetting the list, whereas refresh() reloads everything; re-creating the Pager or re-collecting is unnecessary and loses paging state.
When Paging invalidates a PagingSource and builds a new one, how does getRefreshKey() keep the user near their current scroll position?
- It always returns the initial key so the list reloads from the very top
- It simply reuses the last nextKey that load() returned
- It derives a key from the closest loaded page's prevKey or nextKey
- It returns PagingState.anchorPosition directly as the new load key
Answer: It derives a key from the closest loaded page's prevKey or nextKey
getRefreshKey() reads PagingState.anchorPosition (the most recently accessed index) and computes a key from the closest loaded page's prevKey/nextKey; the anchorPosition itself is an index, not a usable load key.
What does Paging do when a PagingSource's load() returns LoadResult.Invalid?
- It keeps the current results and shows the error inline
- It retries the same load() call after a short backoff delay
- It permanently disables placeholders for the entire list
- It drops the PagingSource and rebuilds it from the factory
Answer: It drops the PagingSource and rebuilds it from the factory
LoadResult.Invalid signals the source is stale, so Paging throws away its results and invalidates it, prompting the Pager factory to instantiate a new PagingSource rather than retrying the old one.
For enablePlaceholders = true to actually render placeholders, what must the data source supply?
- A known total item count so Paging can emit null for gaps
- A stable, unique item key supplied for every single row
- A prefetchDistance value configured to exactly zero
- A RemoteMediator implementation backing the Pager object
Answer: A known total item count so Paging can emit null for gaps
Placeholders require Paging to know the full item count up front (automatic with a Room-backed source); a custom PagingSource that does not report a count silently has placeholders disabled.
Where should PagingData operators like map and insertSeparators be applied in a ViewModel chain?
- After cachedIn(), so each new collector recomputes them
- Only inside PagingSource.load(), where paging data is loaded
- Before cachedIn(), so the transformed output is cached
- Inside the Composable, instead of keeping it in the ViewModel
Answer: Before cachedIn(), so the transformed output is cached
PagingData transformations execute for every emission, so applying them before cachedIn() caches the transformed output; placing them after cachedIn() forces them to recompute for each new collector.
What causes Paging to call load() for an APPEND?
- A manual append() call triggered directly from the UI layer
- The user scrolling within prefetchDistance of the loaded edge
- A timer firing every pageSize milliseconds to fetch more data
- The PagingSource returning LoadResult.Invalid during reload
Answer: The user scrolling within prefetchDistance of the loaded edge
Paging automatically requests the next page once an accessed item comes within prefetchDistance of the loaded edge; there is no manual append() call, and load triggering is access-driven, not time-driven.
What is the purpose of overriding RemoteMediator.initialize()?
- To construct the Room database instance before paging starts
- To provide the PagingConfig values used when creating a Pager
- To map database entities into UI models for display in the app
- To return an InitializeAction that decides refresh vs. skip
Answer: To return an InitializeAction that decides refresh vs. skip
initialize() returns LAUNCH_INITIAL_REFRESH or SKIP_INITIAL_REFRESH; skipping lets you serve still-fresh cached DB data immediately and avoid an unnecessary initial network refresh.
Which statement best describes a PagingData<T>?
- An immutable List<T> holding every loaded item, ready for synchronous indexing
- A snapshot of paged data emitted as updates, not a persistent collection
- A Room DAO that lazily queries database rows on demand as pages are requested
- A RecyclerView.Adapter subclass that directly binds rows for display
Answer: A snapshot of paged data emitted as updates, not a persistent collection
PagingData is a self-contained snapshot of paged data emitted through a Flow; each emission is an update consumed via an adapter or collectAsLazyPagingItems, not a static indexable list.