501 lines
23 KiB
Plaintext
501 lines
23 KiB
Plaintext
page.title=Loaders
|
||
parent.title=Activities
|
||
parent.link=activities.html
|
||
@jd:body
|
||
<div id="qv-wrapper">
|
||
<div id="qv">
|
||
<h2>In this document</h2>
|
||
<ol>
|
||
<li><a href="#summary">Loader API Summary</a></li>
|
||
<li><a href="#app">Using Loaders in an Application</a>
|
||
<ol>
|
||
<li><a href="#requirements"></a></li>
|
||
<li><a href="#starting">Starting a Loader</a></li>
|
||
<li><a href="#restarting">Restarting a Loader</a></li>
|
||
<li><a href="#callback">Using the LoaderManager Callbacks</a></li>
|
||
</ol>
|
||
</li>
|
||
<li><a href="#example">Example</a>
|
||
<ol>
|
||
<li><a href="#more_examples">More Examples</a></li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
|
||
<h2>Key classes</h2>
|
||
<ol>
|
||
<li>{@link android.app.LoaderManager}</li>
|
||
<li>{@link android.content.Loader}</li>
|
||
|
||
</ol>
|
||
|
||
<h2>Related samples</h2>
|
||
<ol>
|
||
<li> <a
|
||
href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">
|
||
LoaderCursor</a></li>
|
||
<li> <a
|
||
href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
|
||
LoaderThrottle</a></li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
|
||
<p>Introduced in Android 3.0, loaders make it easy to asynchronously load data
|
||
in an activity or fragment. Loaders have these characteristics:</p>
|
||
<ul>
|
||
<li>They are available to every {@link android.app.Activity} and {@link
|
||
android.app.Fragment}.</li>
|
||
<li>They provide asynchronous loading of data.</li>
|
||
<li>They monitor the source of their data and deliver new results when the
|
||
content changes.</li>
|
||
<li>They automatically reconnect to the last loader's cursor when being
|
||
recreated after a configuration change. Thus, they don't need to re-query their
|
||
data.</li>
|
||
</ul>
|
||
|
||
<h2 id="summary">Loader API Summary</h2>
|
||
|
||
<p>There are multiple classes and interfaces that may be involved in using
|
||
loaders in an application. They are summarized in this table:</p>
|
||
|
||
<table>
|
||
<tr>
|
||
<th>Class/Interface</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
<tr>
|
||
<td>{@link android.app.LoaderManager}</td>
|
||
<td>An abstract class associated with an {@link android.app.Activity} or
|
||
{@link android.app.Fragment} for managing one or more {@link
|
||
android.content.Loader} instances. This helps an application manage
|
||
longer-running operations in conjunction with the {@link android.app.Activity}
|
||
or {@link android.app.Fragment} lifecycle; the most common use of this is with a
|
||
{@link android.content.CursorLoader}, however applications are free to write
|
||
their own loaders for loading other types of data.
|
||
<br />
|
||
<br />
|
||
There is only one {@link android.app.LoaderManager} per activity or fragment. But a {@link android.app.LoaderManager} can have
|
||
multiple loaders.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>{@link android.app.LoaderManager.LoaderCallbacks}</td>
|
||
<td>A callback interface for a client to interact with the {@link
|
||
android.app.LoaderManager}. For example, you use the {@link
|
||
android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
|
||
callback method to create a new loader.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>{@link android.content.Loader}</td>
|
||
<td>An abstract class that performs asynchronous loading of data. This is
|
||
the base class for a loader. You would typically use {@link
|
||
android.content.CursorLoader}, but you can implement your own subclass. While
|
||
loaders are active they should monitor the source of their data and deliver new
|
||
results when the contents change. </td>
|
||
</tr>
|
||
<tr>
|
||
<td>{@link android.content.AsyncTaskLoader}</td>
|
||
<td>Abstract loader that provides an {@link android.os.AsyncTask} to do the work.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>{@link android.content.CursorLoader}</td>
|
||
<td>A subclass of {@link android.content.AsyncTaskLoader} that queries the
|
||
{@link android.content.ContentResolver} and returns a {@link
|
||
android.database.Cursor}. This class implements the {@link
|
||
android.content.Loader} protocol in a standard way for querying cursors,
|
||
building on {@link android.content.AsyncTaskLoader} to perform the cursor query
|
||
on a background thread so that it does not block the application's UI. Using
|
||
this loader is the best way to asynchronously load data from a {@link
|
||
android.content.ContentProvider}, instead of performing a managed query through
|
||
the fragment or activity's APIs.</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<p>The classes and interfaces in the above table are the essential components
|
||
you'll use to implement a loader in your application. You won't need all of them
|
||
for each loader you create, but you'll always need a reference to the {@link
|
||
android.app.LoaderManager} in order to initialize a loader and an implementation
|
||
of a {@link android.content.Loader} class such as {@link
|
||
android.content.CursorLoader}. The following sections show you how to use these
|
||
classes and interfaces in an application.</p>
|
||
|
||
<h2 id ="app">Using Loaders in an Application</h2>
|
||
<p>This section describes how to use loaders in an Android application. An
|
||
application that uses loaders typically includes the following:</p>
|
||
<ul>
|
||
<li>An {@link android.app.Activity} or {@link android.app.Fragment}.</li>
|
||
<li>An instance of the {@link android.app.LoaderManager}.</li>
|
||
<li>A {@link android.content.CursorLoader} to load data backed by a {@link
|
||
android.content.ContentProvider}. Alternatively, you can implement your own subclass
|
||
of {@link android.content.Loader} or {@link android.content.AsyncTaskLoader} to
|
||
load data from some other source.</li>
|
||
<li>An implementation for {@link android.app.LoaderManager.LoaderCallbacks}.
|
||
This is where you create new loaders and manage your references to existing
|
||
loaders.</li>
|
||
<li>A way of displaying the loader's data, such as a {@link
|
||
android.widget.SimpleCursorAdapter}.</li>
|
||
<li>A data source, such as a {@link android.content.ContentProvider}, when using a
|
||
{@link android.content.CursorLoader}.</li>
|
||
</ul>
|
||
<h3 id="starting">Starting a Loader</h3>
|
||
|
||
<p>The {@link android.app.LoaderManager} manages one or more {@link
|
||
android.content.Loader} instances within an {@link android.app.Activity} or
|
||
{@link android.app.Fragment}. There is only one {@link
|
||
android.app.LoaderManager} per activity or fragment.</p>
|
||
|
||
<p>You typically
|
||
initialize a {@link android.content.Loader} within the activity's {@link
|
||
android.app.Activity#onCreate onCreate()} method, or within the fragment's
|
||
{@link android.app.Fragment#onActivityCreated onActivityCreated()} method. You
|
||
do this as follows:</p>
|
||
|
||
<pre>// Prepare the loader. Either re-connect with an existing one,
|
||
// or start a new one.
|
||
getLoaderManager().initLoader(0, null, this);</pre>
|
||
|
||
<p>The {@link android.app.LoaderManager#initLoader initLoader()} method takes
|
||
the following parameters:</p>
|
||
<ul>
|
||
<li>A unique ID that identifies the loader. In this example, the ID is 0.</li>
|
||
<li>Optional arguments to supply to the loader at
|
||
construction (<code>null</code> in this example).</li>
|
||
|
||
<li>A {@link android.app.LoaderManager.LoaderCallbacks} implementation, which
|
||
the {@link android.app.LoaderManager} calls to report loader events. In this
|
||
example, the local class implements the {@link
|
||
android.app.LoaderManager.LoaderCallbacks} interface, so it passes a reference
|
||
to itself, {@code this}.</li>
|
||
</ul>
|
||
<p>The {@link android.app.LoaderManager#initLoader initLoader()} call ensures that a loader
|
||
is initialized and active. It has two possible outcomes:</p>
|
||
<ul>
|
||
<li>If the loader specified by the ID already exists, the last created loader
|
||
is reused.</li>
|
||
<li>If the loader specified by the ID does <em>not</em> exist,
|
||
{@link android.app.LoaderManager#initLoader initLoader()} triggers the
|
||
{@link android.app.LoaderManager.LoaderCallbacks} method {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}.
|
||
This is where you implement the code to instantiate and return a new loader.
|
||
For more discussion, see the section <a
|
||
href="#onCreateLoader">onCreateLoader</a>.</li>
|
||
</ul>
|
||
<p>In either case, the given {@link android.app.LoaderManager.LoaderCallbacks}
|
||
implementation is associated with the loader, and will be called when the
|
||
loader state changes. If at the point of this call the caller is in its
|
||
started state, and the requested loader already exists and has generated its
|
||
data, then the system calls {@link
|
||
android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
|
||
immediately (during {@link android.app.LoaderManager#initLoader initLoader()}),
|
||
so you must be prepared for this to happen. See <a href="#onLoadFinished">
|
||
onLoadFinished</a> for more discussion of this callback</p>
|
||
|
||
<p>Note that the {@link android.app.LoaderManager#initLoader initLoader()}
|
||
method returns the {@link android.content.Loader} that is created, but you don't
|
||
need to capture a reference to it. The {@link android.app.LoaderManager} manages
|
||
the life of the loader automatically. The {@link android.app.LoaderManager}
|
||
starts and stops loading when necessary, and maintains the state of the loader
|
||
and its associated content. As this implies, you rarely interact with loaders
|
||
directly (though for an example of using loader methods to fine-tune a loader's
|
||
behavior, see the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> sample).
|
||
You most commonly use the {@link
|
||
android.app.LoaderManager.LoaderCallbacks} methods to intervene in the loading
|
||
process when particular events occur. For more discussion of this topic, see <a
|
||
href="#callback">Using the LoaderManager Callbacks</a>.</p>
|
||
|
||
<h3 id="restarting">Restarting a Loader</h3>
|
||
|
||
<p>When you use {@link android.app.LoaderManager#initLoader initLoader()}, as
|
||
shown above, it uses an existing loader with the specified ID if there is one.
|
||
If there isn't, it creates one. But sometimes you want to discard your old data
|
||
and start over.</p>
|
||
|
||
<p>To discard your old data, you use {@link
|
||
android.app.LoaderManager#restartLoader restartLoader()}. For example, this
|
||
implementation of {@link android.widget.SearchView.OnQueryTextListener} restarts
|
||
the loader when the user's query changes. The loader needs to be restarted so
|
||
that it can use the revised search filter to do a new query:</p>
|
||
|
||
<pre>
|
||
public boolean onQueryTextChanged(String newText) {
|
||
// Called when the action bar search text has changed. Update
|
||
// the search filter, and restart the loader to do a new query
|
||
// with this filter.
|
||
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
|
||
getLoaderManager().restartLoader(0, null, this);
|
||
return true;
|
||
}</pre>
|
||
|
||
<h3 id="callback">Using the LoaderManager Callbacks</h3>
|
||
|
||
<p>{@link android.app.LoaderManager.LoaderCallbacks} is a callback interface
|
||
that lets a client interact with the {@link android.app.LoaderManager}. </p>
|
||
<p>Loaders, in particular {@link android.content.CursorLoader}, are expected to
|
||
retain their data after being stopped. This allows applications to keep their
|
||
data across the activity or fragment's {@link android.app.Activity#onStop
|
||
onStop()} and {@link android.app.Activity#onStart onStart()} methods, so that
|
||
when users return to an application, they don't have to wait for the data to
|
||
reload. You use the {@link android.app.LoaderManager.LoaderCallbacks} methods
|
||
when to know when to create a new loader, and to tell the application when it is
|
||
time to stop using a loader's data.</p>
|
||
|
||
<p>{@link android.app.LoaderManager.LoaderCallbacks} includes these
|
||
methods:</p>
|
||
<ul>
|
||
<li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} —
|
||
Instantiate and return a new {@link android.content.Loader} for the given ID.
|
||
</li></ul>
|
||
<ul>
|
||
<li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
|
||
— Called when a previously created loader has finished its load.
|
||
</li></ul>
|
||
<ul>
|
||
<li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}
|
||
— Called when a previously created loader is being reset, thus making its
|
||
data unavailable.
|
||
</li>
|
||
</ul>
|
||
<p>These methods are described in more detail in the following sections.</p>
|
||
|
||
<h4 id ="onCreateLoader">onCreateLoader</h4>
|
||
|
||
<p>When you attempt to access a loader (for example, through {@link
|
||
android.app.LoaderManager#initLoader initLoader()}), it checks to see whether
|
||
the loader specified by the ID exists. If it doesn't, it triggers the {@link
|
||
android.app.LoaderManager.LoaderCallbacks} method {@link
|
||
android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. This
|
||
is where you create a new loader. Typically this will be a {@link
|
||
android.content.CursorLoader}, but you can implement your own {@link
|
||
android.content.Loader} subclass. </p>
|
||
|
||
<p>In this example, the {@link
|
||
android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
|
||
callback method creates a {@link android.content.CursorLoader}. You must build
|
||
the {@link android.content.CursorLoader} using its constructor method, which
|
||
requires the complete set of information needed to perform a query to the {@link
|
||
android.content.ContentProvider}. Specifically, it needs:</p>
|
||
<ul>
|
||
<li><em>uri</em> — The URI for the content to retrieve. </li>
|
||
<li><em>projection</em> — A list of which columns to return. Passing
|
||
<code>null</code> will return all columns, which is inefficient. </li>
|
||
<li><em>selection</em> — A filter declaring which rows to return,
|
||
formatted as an SQL WHERE clause (excluding the WHERE itself). Passing
|
||
<code>null</code> will return all rows for the given URI. </li>
|
||
<li><em>selectionArgs</em> — You may include ?s in the selection, which will
|
||
be replaced by the values from <em>selectionArgs</em>, in the order that they appear in
|
||
the selection. The values will be bound as Strings. </li>
|
||
<li><em>sortOrder</em> — How to order the rows, formatted as an SQL
|
||
ORDER BY clause (excluding the ORDER BY itself). Passing <code>null</code> will
|
||
use the default sort order, which may be unordered.</li>
|
||
</ul>
|
||
<p>For example:</p>
|
||
<pre>
|
||
// If non-null, this is the current filter the user has provided.
|
||
String mCurFilter;
|
||
...
|
||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||
// This is called when a new Loader needs to be created. This
|
||
// sample only has one Loader, so we don't care about the ID.
|
||
// First, pick the base URI to use depending on whether we are
|
||
// currently filtering.
|
||
Uri baseUri;
|
||
if (mCurFilter != null) {
|
||
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
|
||
Uri.encode(mCurFilter));
|
||
} else {
|
||
baseUri = Contacts.CONTENT_URI;
|
||
}
|
||
|
||
// Now create and return a CursorLoader that will take care of
|
||
// creating a Cursor for the data being displayed.
|
||
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
|
||
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
|
||
+ Contacts.DISPLAY_NAME + " != '' ))";
|
||
return new CursorLoader(getActivity(), baseUri,
|
||
CONTACTS_SUMMARY_PROJECTION, select, null,
|
||
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
|
||
}</pre>
|
||
<h4 id="onLoadFinished">onLoadFinished</h4>
|
||
|
||
<p>This method is called when a previously created loader has finished its load.
|
||
This method is guaranteed to be called prior to the release of the last data
|
||
that was supplied for this loader. At this point you should remove all use of
|
||
the old data (since it will be released soon), but should not do your own
|
||
release of the data since its loader owns it and will take care of that.</p>
|
||
|
||
|
||
<p>The loader will release the data once it knows the application is no longer
|
||
using it. For example, if the data is a cursor from a {@link
|
||
android.content.CursorLoader}, you should not call {@link
|
||
android.database.Cursor#close close()} on it yourself. If the cursor is being
|
||
placed in a {@link android.widget.CursorAdapter}, you should use the {@link
|
||
android.widget.SimpleCursorAdapter#swapCursor swapCursor()} method so that the
|
||
old {@link android.database.Cursor} is not closed. For example:</p>
|
||
|
||
<pre>
|
||
// This is the Adapter being used to display the list's data.<br
|
||
/>SimpleCursorAdapter mAdapter;
|
||
...
|
||
|
||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||
// Swap the new cursor in. (The framework will take care of closing the
|
||
// old cursor once we return.)
|
||
mAdapter.swapCursor(data);
|
||
}</pre>
|
||
|
||
<h4 id="onLoaderReset">onLoaderReset</h4>
|
||
|
||
<p>This method is called when a previously created loader is being reset, thus
|
||
making its data unavailable. This callback lets you find out when the data is
|
||
about to be released so you can remove your reference to it. </p>
|
||
<p>This implementation calls
|
||
{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}
|
||
with a value of <code>null</code>:</p>
|
||
|
||
<pre>
|
||
// This is the Adapter being used to display the list's data.
|
||
SimpleCursorAdapter mAdapter;
|
||
...
|
||
|
||
public void onLoaderReset(Loader<Cursor> loader) {
|
||
// This is called when the last Cursor provided to onLoadFinished()
|
||
// above is about to be closed. We need to make sure we are no
|
||
// longer using it.
|
||
mAdapter.swapCursor(null);
|
||
}</pre>
|
||
|
||
|
||
<h2 id="example">Example</h2>
|
||
|
||
<p>As an example, here is the full implementation of a {@link
|
||
android.app.Fragment} that displays a {@link android.widget.ListView} containing
|
||
the results of a query against the contacts content provider. It uses a {@link
|
||
android.content.CursorLoader} to manage the query on the provider.</p>
|
||
|
||
<p>For an application to access a user's contacts, as shown in this example, its
|
||
manifest must include the permission
|
||
{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.</p>
|
||
|
||
<pre>
|
||
public static class CursorLoaderListFragment extends ListFragment
|
||
implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
|
||
|
||
// This is the Adapter being used to display the list's data.
|
||
SimpleCursorAdapter mAdapter;
|
||
|
||
// If non-null, this is the current filter the user has provided.
|
||
String mCurFilter;
|
||
|
||
@Override public void onActivityCreated(Bundle savedInstanceState) {
|
||
super.onActivityCreated(savedInstanceState);
|
||
|
||
// Give some text to display if there is no data. In a real
|
||
// application this would come from a resource.
|
||
setEmptyText("No phone numbers");
|
||
|
||
// We have a menu item to show in action bar.
|
||
setHasOptionsMenu(true);
|
||
|
||
// Create an empty adapter we will use to display the loaded data.
|
||
mAdapter = new SimpleCursorAdapter(getActivity(),
|
||
android.R.layout.simple_list_item_2, null,
|
||
new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
|
||
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
|
||
setListAdapter(mAdapter);
|
||
|
||
// Prepare the loader. Either re-connect with an existing one,
|
||
// or start a new one.
|
||
getLoaderManager().initLoader(0, null, this);
|
||
}
|
||
|
||
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||
// Place an action bar item for searching.
|
||
MenuItem item = menu.add("Search");
|
||
item.setIcon(android.R.drawable.ic_menu_search);
|
||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||
SearchView sv = new SearchView(getActivity());
|
||
sv.setOnQueryTextListener(this);
|
||
item.setActionView(sv);
|
||
}
|
||
|
||
public boolean onQueryTextChange(String newText) {
|
||
// Called when the action bar search text has changed. Update
|
||
// the search filter, and restart the loader to do a new query
|
||
// with this filter.
|
||
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
|
||
getLoaderManager().restartLoader(0, null, this);
|
||
return true;
|
||
}
|
||
|
||
@Override public boolean onQueryTextSubmit(String query) {
|
||
// Don't care about this.
|
||
return true;
|
||
}
|
||
|
||
@Override public void onListItemClick(ListView l, View v, int position, long id) {
|
||
// Insert desired behavior here.
|
||
Log.i("FragmentComplexList", "Item clicked: " + id);
|
||
}
|
||
|
||
// These are the Contacts rows that we will retrieve.
|
||
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
|
||
Contacts._ID,
|
||
Contacts.DISPLAY_NAME,
|
||
Contacts.CONTACT_STATUS,
|
||
Contacts.CONTACT_PRESENCE,
|
||
Contacts.PHOTO_ID,
|
||
Contacts.LOOKUP_KEY,
|
||
};
|
||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||
// This is called when a new Loader needs to be created. This
|
||
// sample only has one Loader, so we don't care about the ID.
|
||
// First, pick the base URI to use depending on whether we are
|
||
// currently filtering.
|
||
Uri baseUri;
|
||
if (mCurFilter != null) {
|
||
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
|
||
Uri.encode(mCurFilter));
|
||
} else {
|
||
baseUri = Contacts.CONTENT_URI;
|
||
}
|
||
|
||
// Now create and return a CursorLoader that will take care of
|
||
// creating a Cursor for the data being displayed.
|
||
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
|
||
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
|
||
+ Contacts.DISPLAY_NAME + " != '' ))";
|
||
return new CursorLoader(getActivity(), baseUri,
|
||
CONTACTS_SUMMARY_PROJECTION, select, null,
|
||
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
|
||
}
|
||
|
||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||
// Swap the new cursor in. (The framework will take care of closing the
|
||
// old cursor once we return.)
|
||
mAdapter.swapCursor(data);
|
||
}
|
||
|
||
public void onLoaderReset(Loader<Cursor> loader) {
|
||
// This is called when the last Cursor provided to onLoadFinished()
|
||
// above is about to be closed. We need to make sure we are no
|
||
// longer using it.
|
||
mAdapter.swapCursor(null);
|
||
}
|
||
}</pre>
|
||
<h3 id="more_examples">More Examples</h3>
|
||
|
||
<p>There are a few different samples in <strong>ApiDemos</strong> that
|
||
illustrate how to use loaders:</p>
|
||
<ul>
|
||
<li><a
|
||
href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">
|
||
LoaderCursor</a> — A complete version of the
|
||
snippet shown above.</li>
|
||
<li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> — An example of how to use throttling to
|
||
reduce the number of queries a content provider does when its data changes.</li>
|
||
</ul>
|
||
|
||
<p>For information on downloading and installing the SDK samples, see <a
|
||
href="http://developer.android.com/resources/samples/get.html"> Getting the
|
||
Samples</a>. </p>
|
||
|