Android Training: Loading Data in the Background
Change-Id: Ibb469cd068068b37ea95371afb7e4dca4535ba94
diff --git a/docs/html/training/load-data-background/handle-results.jd b/docs/html/training/load-data-background/handle-results.jd
new file mode 100644
index 0000000..ce0024f
--- /dev/null
+++ b/docs/html/training/load-data-background/handle-results.jd
@@ -0,0 +1,137 @@
+page.title=Handling the Results
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+ <div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li>
+ <a href="#HandleResults">Handle Query Results</a>
+ </li>
+ <li>
+ <a href="#HandleReset">Delete Old Cursor References</a></li>
+</ol>
+
+<h2>Try it out</h2>
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
+ <p class="filename">ThreadSample.zip</p>
+</div>
+
+ </div>
+</div>
+
+<p>
+ As shown in the previous lesson, you should begin loading your data with a
+ {@link android.support.v4.content.CursorLoader} in your implementation of
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
+ onCreateLoader()}. The loader then provides the query results to your
+ {@link android.app.Activity} or {@link android.support.v4.app.FragmentActivity} in your
+ implementation of {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished
+ LoaderCallbacks.onLoadFinished()}. One of the incoming arguments to this method is a
+ {@link android.database.Cursor} containing the query results. You can use this object to
+ update your data display or do further processing.
+</p>
+<p>
+ Besides
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} and
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()},
+ you also have to implement
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}.
+ This method is invoked when {@link android.support.v4.content.CursorLoader} detects
+ that data associated with the {@link android.database.Cursor} has changed. When the
+ data changes, the framework also re-runs the current query.
+</p>
+<h2 id="HandleResults">Handle Query Results</h2>
+<p>
+ To display {@link android.database.Cursor} data returned by
+ {@link android.support.v4.content.CursorLoader}, use a
+ {@link android.view.View} class that implements {@link android.widget.AdapterView} and
+ provide the view with an adapter that implements
+ {@link android.support.v4.widget.CursorAdapter}. The system then automatically moves data from
+ the {@link android.database.Cursor} to the view.
+</p>
+<p>
+ You can set up the linkage between the view and adapter before you have any data to display,
+ and then move a {@link android.database.Cursor} into the adapter in the
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+ method. As soon as you move the {@link android.database.Cursor} into the adapter, the
+ system automatically updates the view. This also happens if you change the contents of the
+ {@link android.database.Cursor}.
+</p>
+<p>
+ For example:
+</p>
+<pre>
+public String[] mFromColumns = {
+ DataProviderContract.IMAGE_PICTURENAME_COLUMN
+};
+public int[] mToFields = {
+ R.id.PictureName
+};
+// Gets a handle to a List View
+ListView mListView = (ListView) findViewById(R.id.dataList);
+/*
+ * Defines a SimpleCursorAdapter for the ListView
+ *
+ */
+SimpleCursorAdapter mAdapter =
+ new SimpleCursorAdapter(
+ this, // Current context
+ R.layout.list_item, // Layout for a single row
+ null, // No Cursor yet
+ mFromColumns, // Cursor columns to use
+ mToFields, // Layout fields to use
+ 0 // No flags
+ );
+// Sets the adapter for the view
+mListView.setAdapter(mAdapter);
+...
+/*
+ * Defines the callback that {@link android.support.v4.content.CursorLoader} calls
+ * when it's finished its query
+ */
+@Override
+public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+ ...
+ /*
+ * Moves the query results into the adapter, causing the
+ * ListView fronting this adapter to re-display
+ */
+ mAdapter.changeCursor(cursor);
+}
+</pre>
+<h2 id="HandleReset">Delete Old Cursor References</h2>
+<p>
+ The {@link android.support.v4.content.CursorLoader} is reset whenever its
+ {@link android.database.Cursor} becomes invalid. This usually occurs because the data associated
+ with the {@link android.database.Cursor} has changed. Before re-running the query,
+ the framework calls your implementation of
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}. In
+ this callback, you should delete all references to the current {@link android.database.Cursor}
+ in order to prevent memory leaks. Once
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}
+ finishes, {@link android.support.v4.content.CursorLoader} re-runs its query.
+</p>
+<p>
+ For example:
+</p>
+<pre>
+/*
+ * Invoked when the CursorLoader is being reset. For example, this is
+ * called if the data in the provider changes and the Cursor becomes stale.
+ */
+@Override
+public void onLoaderReset(Loader<Cursor> loader) {
+
+ /*
+ * Clears out the adapter's reference to the Cursor.
+ * This prevents memory leaks.
+ */
+ mAdapter.changeCursor(null);
+}
+</pre>
diff --git a/docs/html/training/load-data-background/index.jd b/docs/html/training/load-data-background/index.jd
new file mode 100644
index 0000000..dc9d84a
--- /dev/null
+++ b/docs/html/training/load-data-background/index.jd
@@ -0,0 +1,77 @@
+page.title=Loading Data in the Background
+trainingnavtop=true
+startpage=true
+
+@jd:body
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>
+ Android 1.6 or later
+ </li>
+</ul>
+
+<!-- related docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/components/loaders.html">Loaders</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/data/data-storage.html#db">Using Databases</a>
+ </li>
+ <li>
+<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">Content Provider Basics</a>
+ </li>
+</ul>
+
+<h2>Try it out</h2>
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
+ <p class="filename">ThreadSample.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+ Querying a {@link android.content.ContentProvider} for data you want to display takes time.
+ If you run the query directly from an {@link android.app.Activity}, it may get blocked and
+ cause the system to issue an "Application Not Responding" message. Even if it doesn't, users
+ will see an annoying delay in the UI. To avoid these problems, you should initiate a query on a
+ separate thread, wait for it to finish, and then display the results.
+</p>
+<p>
+ You can do this in a straightforward way by using an object that runs a query asynchronously in
+ the background and reconnects to your {@link android.app.Activity} when it's finished. This
+ object is a {@link android.support.v4.content.CursorLoader}. Besides doing the initial
+ background query, a {@link android.support.v4.content.CursorLoader} automatically re-runs the
+ query when data associated with the query changes.
+</p>
+<p>
+ This class describes how to use a {@link android.support.v4.content.CursorLoader} to run a
+ background query. Examples in this class use the {@link android.support.v4 v4 support library}
+ versions of classes, which support platforms starting with Android 1.6.
+</p>
+<h2>Lessons</h2>
+<dl>
+ <dt>
+ <strong><a href="setup-loader.html">Running a Query with a CursorLoader</a></strong>
+ </dt>
+ <dd>
+ Learn how to run a query in the background, using a
+ {@link android.support.v4.content.CursorLoader}.
+ </dd>
+ <dt>
+ <strong>
+ <a href="handle-results.html">Handling the Results</a>
+ </strong>
+ </dt>
+ <dd>
+ Learn how to handle the {@link android.database.Cursor} returned from the query, and how
+ to remove references to the current {@link android.database.Cursor} when the loader
+ framework re-sets the {@link android.support.v4.content.CursorLoader}.
+ </dd>
+</dl>
diff --git a/docs/html/training/load-data-background/setup-loader.jd b/docs/html/training/load-data-background/setup-loader.jd
new file mode 100644
index 0000000..17fe7b0
--- /dev/null
+++ b/docs/html/training/load-data-background/setup-loader.jd
@@ -0,0 +1,142 @@
+page.title=Running a Query with a CursorLoader
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+ <div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li>
+ <a href="#Extend">Define an Activity That Uses CursorLoader</a>
+ </li>
+ <li>
+ <a href="#InitializeLoader">Initialize the Query</a>
+ </li>
+ <li>
+ <a href="#DefineLaunch">Start the Query</a>
+ </li>
+</ol>
+
+<h2>Try it out</h2>
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
+ <p class="filename">ThreadSample.zip</p>
+</div>
+
+ </div>
+</div>
+<p>
+ A {@link android.support.v4.content.CursorLoader} runs an asynchronous query in the background
+ against a {@link android.content.ContentProvider}, and returns the results to the
+ {@link android.app.Activity} or {@link android.support.v4.app.FragmentActivity} from which it
+ was called. This allows the {@link android.app.Activity} or
+ {@link android.support.v4.app.FragmentActivity} to continue to interact with the user while the
+ query is ongoing.
+</p>
+<h2 id="Extend">Define an Activity That Uses CursorLoader</h2>
+<p>
+ To use a {@link android.support.v4.content.CursorLoader} with an
+ {@link android.app.Activity} or {@link android.support.v4.app.FragmentActivity}, use the
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks LoaderCallbacks<Cursor>}
+ interface. A {@link android.support.v4.content.CursorLoader} invokes callbacks defined
+ in this interface to communicate with the class; this lesson and the next one
+ describe each callback in detail.
+</p>
+<p>
+ For example, this is how you should define a {@link android.support.v4.app.FragmentActivity}
+ that uses the support library version of {@link android.support.v4.content.CursorLoader}. By
+ extending {@link android.support.v4.app.FragmentActivity}, you get support for
+ {@link android.support.v4.content.CursorLoader} as well as
+ {@link android.support.v4.app.Fragment}:
+</p>
+<pre>
+public class PhotoThumbnailFragment extends FragmentActivity implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+...
+}
+</pre>
+<h2 id="InitializeLoader">Initialize the Query</h2>
+<p>
+ To initialize a query, call
+ {@link android.support.v4.app.LoaderManager#initLoader LoaderManager.initLoader()}. This
+ initializes the background framework. You can do this after the user has entered data that's
+ used in the query, or, if you don't need any user data, you can do it in
+ {@link android.support.v4.app.FragmentActivity#onCreate onCreate()} or
+ {@link android.support.v4.app.Fragment#onCreateView onCreateView()}. For example:
+</p>
+<pre>
+ // Identifies a particular Loader being used in this component
+ private static final int URL_LOADER = 0;
+ ...
+ /* When the system is ready for the Fragment to appear, this displays
+ * the Fragment's View
+ */
+ public View onCreateView(
+ LayoutInflater inflater,
+ ViewGroup viewGroup,
+ Bundle bundle) {
+ ...
+ /*
+ * Initializes the CursorLoader. The URL_LOADER value is eventually passed
+ * to onCreateLoader().
+ */
+ getLoaderManager().initLoader(URL_LOADER, null, this);
+ ...
+ }
+</pre>
+<p class="note">
+ <strong>Note:</strong> The method {@link android.support.v4.app.Fragment#getLoaderManager
+ getLoaderManager()} is only available in the {@link android.support.v4.app.Fragment} class. To
+ get a {@link android.support.v4.app.LoaderManager} in a
+ {@link android.support.v4.app.FragmentActivity}, call
+ {@link android.support.v4.app.FragmentActivity#getSupportLoaderManager
+ getSupportLoaderManager()}.
+</p>
+<h2 id="DefineLaunch">Start the Query</h2>
+<p>
+ As soon as the background framework is initialized, it calls your implementation of
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}.
+ To start the query, return a {@link android.support.v4.content.CursorLoader} from this method.
+ You can instantiate an empty {@link android.support.v4.content.CursorLoader} and then use its
+ methods to define your query, or you can instantiate the object and define the query at the
+ same time:
+</p>
+<pre>
+/*
+* Callback that's invoked when the system has initialized the Loader and
+* is ready to start the query. This usually happens when initLoader() is
+* called. The loaderID argument contains the ID value passed to the
+* initLoader() call.
+*/
+@Override
+public Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle)
+{
+ /*
+ * Takes action based on the ID of the Loader that's being created
+ */
+ switch (loaderID) {
+ case URL_LOADER:
+ // Returns a new CursorLoader
+ return new CursorLoader(
+ getActivity(), // Parent activity context
+ mDataUrl, // Table to query
+ mProjection, // Projection to return
+ null, // No selection clause
+ null, // No selection arguments
+ null // Default sort order
+ );
+ default:
+ // An invalid id was passed in
+ return null;
+ }
+}
+</pre>
+<p>
+ Once the background framework has the object, it starts the query in the background. When the
+ query is done, the background framework calls
+ {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()},
+ which is described in the next lesson.
+</p>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index f1fde86..e07e669 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -998,8 +998,6 @@
</li>
</ul>
</li>
-
-
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>training/monetization/index.html"
@@ -1020,7 +1018,6 @@
<!-- End best Publishing -->
</ul><!-- nav -->
-
<script type="text/javascript">
<!--
buildToggleLists();