Android Training: Loading Data in the Background
Change-Id: Ibb469cd068068b37ea95371afb7e4dca4535ba94
This commit is contained in:
137
docs/html/training/load-data-background/handle-results.jd
Normal file
137
docs/html/training/load-data-background/handle-results.jd
Normal file
@@ -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>
|
||||
77
docs/html/training/load-data-background/index.jd
Normal file
77
docs/html/training/load-data-background/index.jd
Normal file
@@ -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>
|
||||
142
docs/html/training/load-data-background/setup-loader.jd
Normal file
142
docs/html/training/load-data-background/setup-loader.jd
Normal file
@@ -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>
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user