summaryrefslogtreecommitdiff
path: root/android/arch/paging/PagedList.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/arch/paging/PagedList.java')
-rw-r--r--android/arch/paging/PagedList.java119
1 files changed, 104 insertions, 15 deletions
diff --git a/android/arch/paging/PagedList.java b/android/arch/paging/PagedList.java
index 0d5313dd..6a31b689 100644
--- a/android/arch/paging/PagedList.java
+++ b/android/arch/paging/PagedList.java
@@ -27,16 +27,65 @@ import java.util.concurrent.Executor;
/**
* Lazy loading list that pages in content from a {@link DataSource}.
* <p>
- * A PagedList is a lazy loaded list, which presents data from a {@link DataSource}. If the
- * DataSource is counted (returns a valid number from its count method(s)), the PagedList will
- * present {@code null} items in place of not-yet-loaded content to serve as placeholders.
+ * A PagedList is a {@link List} which loads its data in chunks (pages) from a {@link DataSource}.
+ * Items can be accessed with {@link #get(int)}, and further loading can be triggered with
+ * {@link #loadAround(int)}. See {@link PagedListAdapter}, which enables the binding of a PagedList
+ * to a {@link android.support.v7.widget.RecyclerView}.
+ * <h4>Loading Data</h4>
* <p>
- * When {@link #loadAround} is called, items will be loaded in near the passed position. If
- * placeholder {@code null}s are present in the list, they will be replaced as content is loaded.
+ * All data in a PagedList is loaded from its {@link DataSource}. Creating a PagedList loads data
+ * from the DataSource immediately, and should for this reason be done on a background thread. The
+ * constructed PagedList may then be passed to and used on the UI thread. This is done to prevent
+ * passing a list with no loaded content to the UI thread, which should generally not be presented
+ * to the user.
* <p>
- * In this way, PagedList can present data for an unbounded, infinite scrolling list, or a very
- * large but countable list. See {@link PagedListAdapter}, which enables the binding of a PagedList
- * to a RecyclerView. Use {@link Config} to control how many items a PagedList loads, and when.
+ * When {@link #loadAround} is called, items will be loaded in near the passed list index. If
+ * placeholder {@code null}s are present in the list, they will be replaced as content is
+ * loaded. If not, newly loaded items will be inserted at the beginning or end of the list.
+ * <p>
+ * PagedList can present data for an unbounded, infinite scrolling list, or a very large but
+ * countable list. Use {@link Config} to control how many items a PagedList loads, and when.
+ * <p>
+ * If you use {@link LivePagedListProvider} to get a
+ * {@link android.arch.lifecycle.LiveData}&lt;PagedList>, it will initialize PagedLists on a
+ * background thread for you.
+ * <h4>Placeholders</h4>
+ * <p>
+ * There are two ways that PagedList can represent its not-yet-loaded data - with or without
+ * {@code null} placeholders.
+ * <p>
+ * With placeholders, the PagedList is always the full size of the data set. {@code get(N)} returns
+ * the {@code N}th item in the data set, or {@code null} if its not yet loaded.
+ * <p>
+ * Without {@code null} placeholders, the PagedList is the sublist of data that has already been
+ * loaded. The size of the PagedList is the number of currently loaded items, and {@code get(N)}
+ * returns the {@code N}th <em>loaded</em> item. This is not necessarily the {@code N}th item in the
+ * data set.
+ * <p>
+ * Placeholders have several benefits:
+ * <ul>
+ * <li>They express the full sized list to the presentation layer (often a
+ * {@link PagedListAdapter}), and so can support scrollbars (without jumping as pages are
+ * loaded) and fast-scrolling to any position, whether loaded or not.
+ * <li>They avoid the need for a loading spinner at the end of the loaded list, since the list
+ * is always full sized.
+ * </ul>
+ * <p>
+ * They also have drawbacks:
+ * <ul>
+ * <li>Your Adapter (or other presentation mechanism) needs to account for {@code null} items.
+ * This often means providing default values in data you bind to a
+ * {@link android.support.v7.widget.RecyclerView.ViewHolder}.
+ * <li>They don't work well if your item views are of different sizes, as this will prevent
+ * loading items from cross-fading nicely.
+ * <li>They require you to count your data set, which can be expensive or impossible, depending
+ * on where your data comes from.
+ * </ul>
+ * <p>
+ * Placeholders are enabled by default, but can be disabled in two ways. They are disabled if the
+ * DataSource returns {@link DataSource#COUNT_UNDEFINED} from any item counting method, or if
+ * {@code false} is passed to {@link Config.Builder#setEnablePlaceholders(boolean)} when building a
+ * {@link Config}.
*
* @param <T> The type of the entries in the list.
*/
@@ -92,6 +141,18 @@ public abstract class PagedList<T> extends AbstractList<T> {
* Builder class for PagedList.
* <p>
* DataSource, main thread and background executor, and Config must all be provided.
+ * <p>
+ * A valid PagedList may not be constructed without data, so building a PagedList queries
+ * initial data from the data source. This is done because it's generally undesired to present a
+ * PagedList with no data in it to the UI. It's better to present initial data, so that the UI
+ * doesn't show an empty list, or placeholders for a few frames, just before showing initial
+ * content.
+ * <p>
+ * Because PagedLists are initialized with data, PagedLists must be built on a background
+ * thread.
+ * <p>
+ * {@link LivePagedListProvider} does this creation on a background thread automatically, if you
+ * want to receive a {@code LiveData<PagedList<...>>}.
*
* @param <Key> Type of key used to load data from the DataSource.
* @param <Value> Type of items held and loaded by the PagedList.
@@ -174,11 +235,17 @@ public abstract class PagedList<T> extends AbstractList<T> {
* <p>
* This call will initial data and perform any counting needed to initialize the PagedList,
* therefore it should only be called on a worker thread.
+ * <p>
+ * While build() will always return a PagedList, it's important to note that the PagedList
+ * initial load may fail to acquire data from the DataSource. This can happen for example if
+ * the DataSource is invalidated during its initial load. If this happens, the PagedList
+ * will be immediately {@link PagedList#isDetached() detached}, and you can retry
+ * construction (including setting a new DataSource).
*
* @return The newly constructed PagedList
*/
- @NonNull
@WorkerThread
+ @NonNull
public PagedList<Value> build() {
if (mDataSource == null) {
throw new IllegalArgumentException("DataSource required");
@@ -403,6 +470,16 @@ public abstract class PagedList<T> extends AbstractList<T> {
* Defines the number of items loaded at once from the DataSource.
* <p>
* Should be several times the number of visible items onscreen.
+ * <p>
+ * Configuring your page size depends on how your data is being loaded and used. Smaller
+ * page sizes improve memory usage, latency, and avoid GC churn. Larger pages generally
+ * improve loading throughput, to a point
+ * (avoid loading more than 2MB from SQLite at once, since it incurs extra cost).
+ * <p>
+ * If you're loading data for very large, social-media style cards that take up most of
+ * a screen, and your database isn't a bottleneck, 10-20 may make sense. If you're
+ * displaying dozens of items in a tiled grid, which can present items during a scroll
+ * much more quickly, consider closer to 100.
*
* @param pageSize Number of items loaded at once from the DataSource.
* @return this
@@ -414,12 +491,15 @@ public abstract class PagedList<T> extends AbstractList<T> {
/**
* Defines how far from the edge of loaded content an access must be to trigger further
- * loading. Defaults to page size.
- * <p>
- * A value of 0 indicates that no list items will be loaded before they are first
- * requested.
+ * loading.
* <p>
* Should be several times the number of visible items onscreen.
+ * <p>
+ * If not set, defaults to page size.
+ * <p>
+ * A value of 0 indicates that no list items will be loaded until they are specifically
+ * requested. This is generally not recommended, so that users don't observe a
+ * placeholder item (with placeholders) or end of list (without) while scrolling.
*
* @param prefetchDistance Distance the PagedList should prefetch.
* @return this
@@ -432,8 +512,10 @@ public abstract class PagedList<T> extends AbstractList<T> {
/**
* Pass false to disable null placeholders in PagedLists using this Config.
* <p>
- * A PagedList will present null placeholders for not yet loaded content if two
- * contitions are met:
+ * If not set, defaults to true.
+ * <p>
+ * A PagedList will present null placeholders for not-yet-loaded content if two
+ * conditions are met:
* <p>
* 1) Its DataSource can count all unloaded items (so that the number of nulls to
* present is known).
@@ -442,6 +524,13 @@ public abstract class PagedList<T> extends AbstractList<T> {
* <p>
* Call {@code setEnablePlaceholders(false)} to ensure the receiver of the PagedList
* (often a {@link PagedListAdapter}) doesn't need to account for null items.
+ * <p>
+ * If placeholders are disabled, not-yet-loaded content will not be present in the list.
+ * Paging will still occur, but as items are loaded or removed, they will be signaled
+ * as inserts to the {@link PagedList.Callback}.
+ * {@link PagedList.Callback#onChanged(int, int)} will not be issued as part of loading,
+ * though a {@link PagedListAdapter} may still receive change events as a result of
+ * PagedList diffing.
*
* @param enablePlaceholders False if null placeholders should be disabled.
* @return this