• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Loaders
2parent.title=Activities
3parent.link=activities.html
4@jd:body
5<div id="qv-wrapper">
6<div id="qv">
7    <h2>In this document</h2>
8    <ol>
9    <li><a href="#summary">Loader API Summary</a></li>
10    <li><a href="#app">Using Loaders in an Application</a>
11      <ol>
12        <li><a href="#requirements"></a></li>
13        <li><a href="#starting">Starting a Loader</a></li>
14        <li><a href="#restarting">Restarting a Loader</a></li>
15        <li><a href="#callback">Using the LoaderManager Callbacks</a></li>
16      </ol>
17    </li>
18    <li><a href="#example">Example</a>
19       <ol>
20         <li><a href="#more_examples">More Examples</a></li>
21        </ol>
22    </li>
23  </ol>
24
25  <h2>Key classes</h2>
26    <ol>
27      <li>{@link android.app.LoaderManager}</li>
28      <li>{@link android.content.Loader}</li>
29
30    </ol>
31
32    <h2>Related samples</h2>
33   <ol>
34     <li> <a
35href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">
36LoaderCursor</a></li>
37     <li> <a
38href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
39LoaderThrottle</a></li>
40   </ol>
41  </div>
42</div>
43
44<p>Introduced in Android 3.0, loaders make it easy to asynchronously load data
45in an activity or fragment. Loaders have these characteristics:</p>
46  <ul>
47    <li>They are available to every {@link android.app.Activity} and {@link
48android.app.Fragment}.</li>
49    <li>They provide asynchronous loading of data.</li>
50    <li>They monitor the source of their data and deliver new results when the
51content changes.</li>
52    <li>They automatically reconnect to the last loader's cursor when being
53recreated after a configuration change. Thus, they don't need to re-query their
54data.</li>
55  </ul>
56
57<h2 id="summary">Loader API Summary</h2>
58
59<p>There are multiple classes and interfaces that may be involved in using
60loaders in an application. They are summarized in this table:</p>
61
62<table>
63  <tr>
64    <th>Class/Interface</th>
65    <th>Description</th>
66  </tr>
67  <tr>
68    <td>{@link android.app.LoaderManager}</td>
69    <td>An abstract class associated with an {@link android.app.Activity} or
70{@link android.app.Fragment} for managing one or more {@link
71android.content.Loader} instances. This helps an application manage
72longer-running operations in conjunction with the {@link android.app.Activity}
73or {@link android.app.Fragment} lifecycle; the most common use of this is with a
74{@link android.content.CursorLoader}, however applications are free to write
75their own loaders for loading other types of data.
76    <br />
77    <br />
78    There is only one {@link android.app.LoaderManager} per activity or fragment. But a {@link android.app.LoaderManager} can have
79multiple loaders.</td>
80  </tr>
81  <tr>
82    <td>{@link android.app.LoaderManager.LoaderCallbacks}</td>
83    <td>A callback interface for a client to interact with the {@link
84android.app.LoaderManager}. For example, you use the {@link
85android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
86callback method to create a new loader.</td>
87  </tr>
88  <tr>
89    <td>{@link android.content.Loader}</td>
90    <td>An abstract class that performs asynchronous loading of data. This is
91the base class for a loader. You would typically use {@link
92android.content.CursorLoader}, but you can implement your own subclass. While
93loaders are active they should monitor the source of their data and deliver new
94results when the contents change. </td>
95  </tr>
96  <tr>
97    <td>{@link android.content.AsyncTaskLoader}</td>
98    <td>Abstract loader that provides an {@link android.os.AsyncTask} to do the work.</td>
99  </tr>
100  <tr>
101    <td>{@link android.content.CursorLoader}</td>
102    <td>A subclass of {@link android.content.AsyncTaskLoader} that queries the
103{@link android.content.ContentResolver} and returns a {@link
104android.database.Cursor}. This class implements the {@link
105android.content.Loader} protocol in a standard way for querying cursors,
106building on {@link android.content.AsyncTaskLoader} to perform the cursor query
107on a background thread so that it does not block the application's UI. Using
108this loader is the best way to asynchronously load data from a {@link
109android.content.ContentProvider}, instead of performing a managed query through
110the fragment or activity's APIs.</td>
111  </tr>
112</table>
113
114<p>The classes and interfaces in the above table are the essential components
115you'll use to implement a loader in your application. You won't need all of them
116for each loader you create, but you'll always need a reference to the {@link
117android.app.LoaderManager} in order to initialize a loader and an implementation
118of a {@link android.content.Loader} class such as {@link
119android.content.CursorLoader}. The following sections show you how to use these
120classes and interfaces in an application.</p>
121
122<h2 id ="app">Using Loaders in an Application</h2>
123<p>This section describes how to use loaders in an Android application. An
124application that uses loaders typically includes the following:</p>
125<ul>
126  <li>An {@link android.app.Activity} or {@link android.app.Fragment}.</li>
127  <li>An instance of the {@link android.app.LoaderManager}.</li>
128  <li>A {@link android.content.CursorLoader} to load data backed by a {@link
129android.content.ContentProvider}. Alternatively, you can implement your own subclass
130of {@link android.content.Loader} or {@link android.content.AsyncTaskLoader} to
131load data from some other source.</li>
132  <li>An implementation for {@link android.app.LoaderManager.LoaderCallbacks}.
133This is where you create new loaders and manage your references to existing
134loaders.</li>
135<li>A way of displaying the loader's data, such as a {@link
136android.widget.SimpleCursorAdapter}.</li>
137  <li>A data source, such as a {@link android.content.ContentProvider}, when using a
138{@link android.content.CursorLoader}.</li>
139</ul>
140<h3 id="starting">Starting a Loader</h3>
141
142<p>The {@link android.app.LoaderManager} manages one or more {@link
143android.content.Loader} instances within an {@link android.app.Activity} or
144{@link android.app.Fragment}. There is only one {@link
145android.app.LoaderManager} per activity or fragment.</p>
146
147<p>You typically
148initialize a {@link android.content.Loader} within the activity's {@link
149android.app.Activity#onCreate onCreate()} method, or within the fragment's
150{@link android.app.Fragment#onActivityCreated onActivityCreated()} method. You
151do this as follows:</p>
152
153<pre>// Prepare the loader.  Either re-connect with an existing one,
154// or start a new one.
155getLoaderManager().initLoader(0, null, this);</pre>
156
157<p>The {@link android.app.LoaderManager#initLoader initLoader()} method takes
158the following parameters:</p>
159<ul>
160  <li>A unique ID that identifies the loader. In this example, the ID is 0.</li>
161<li>Optional arguments to supply to the loader at
162construction (<code>null</code> in this example).</li>
163
164<li>A {@link android.app.LoaderManager.LoaderCallbacks} implementation, which
165the {@link android.app.LoaderManager} calls to report loader events. In this
166example, the local class implements the {@link
167android.app.LoaderManager.LoaderCallbacks} interface, so it passes a reference
168to itself, {@code this}.</li>
169</ul>
170<p>The {@link android.app.LoaderManager#initLoader initLoader()} call ensures that a loader
171is initialized and active. It has two possible outcomes:</p>
172<ul>
173  <li>If the loader specified by the ID already exists, the last created loader
174is reused.</li>
175  <li>If the loader specified by the ID does <em>not</em> exist,
176{@link android.app.LoaderManager#initLoader initLoader()} triggers the
177{@link android.app.LoaderManager.LoaderCallbacks} method {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}.
178This is where you  implement the code to instantiate and return a new loader.
179For more discussion, see the section <a
180href="#onCreateLoader">onCreateLoader</a>.</li>
181</ul>
182<p>In either case, the given {@link android.app.LoaderManager.LoaderCallbacks}
183implementation is associated with the loader, and  will be called when the
184loader state changes.  If at the point of this call  the caller is in its
185started state, and the requested loader already exists and has generated its
186data, then the system calls {@link
187android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
188immediately (during {@link android.app.LoaderManager#initLoader initLoader()}),
189so you must be prepared for this to happen. See <a href="#onLoadFinished">
190onLoadFinished</a> for more discussion of this callback</p>
191
192<p>Note that the {@link android.app.LoaderManager#initLoader initLoader()}
193method returns the {@link android.content.Loader} that is created, but you don't
194need to capture a reference to it. The {@link android.app.LoaderManager} manages
195the life of the loader automatically. The {@link android.app.LoaderManager}
196starts and stops loading when necessary, and maintains the state of the loader
197and its associated content. As this implies, you rarely interact with loaders
198directly (though for an example of using loader methods to fine-tune a loader's
199behavior, see the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> sample).
200You most commonly use the {@link
201android.app.LoaderManager.LoaderCallbacks} methods to intervene in the loading
202process when particular events occur. For more discussion of this topic, see <a
203href="#callback">Using the LoaderManager Callbacks</a>.</p>
204
205<h3 id="restarting">Restarting a Loader</h3>
206
207<p>When you use {@link android.app.LoaderManager#initLoader initLoader()}, as
208shown above, it uses an existing loader with the specified ID if there is one.
209If there isn't, it creates one. But sometimes you want to discard your old data
210and start over.</p>
211
212<p>To discard your old data, you use {@link
213android.app.LoaderManager#restartLoader restartLoader()}. For example, this
214implementation of {@link android.widget.SearchView.OnQueryTextListener} restarts
215the loader when the user's query changes. The loader needs to be restarted so
216that it can use the revised search filter to do a new query:</p>
217
218<pre>
219public boolean onQueryTextChanged(String newText) {
220    // Called when the action bar search text has changed.  Update
221    // the search filter, and restart the loader to do a new query
222    // with this filter.
223    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
224    getLoaderManager().restartLoader(0, null, this);
225    return true;
226}</pre>
227
228<h3 id="callback">Using the LoaderManager Callbacks</h3>
229
230<p>{@link android.app.LoaderManager.LoaderCallbacks} is a callback interface
231that lets a client  interact with the {@link android.app.LoaderManager}. </p>
232<p>Loaders, in particular {@link android.content.CursorLoader}, are  expected to
233retain their  data after being stopped. This allows applications to keep their
234data across the activity or fragment's {@link android.app.Activity#onStop
235onStop()} and {@link android.app.Activity#onStart onStart()} methods, so that
236when users return to an application, they don't have to wait for the data to
237reload. You use the {@link android.app.LoaderManager.LoaderCallbacks} methods
238when to know when to create a new loader, and to tell the application when it is
239 time to  stop using a loader's data.</p>
240
241<p>{@link android.app.LoaderManager.LoaderCallbacks} includes these
242methods:</p>
243<ul>
244  <li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}  &#8212;
245Instantiate and return a new {@link android.content.Loader} for the given ID.
246</li></ul>
247<ul>
248  <li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
249&#8212; Called when a previously created loader has finished its load.
250</li></ul>
251<ul>
252  <li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}
253    &#8212; Called when a previously created loader is being reset,  thus  making its
254data unavailable.
255</li>
256</ul>
257<p>These methods are described in more detail in the following sections.</p>
258
259<h4 id ="onCreateLoader">onCreateLoader</h4>
260
261<p>When you attempt to access a loader (for example, through {@link
262android.app.LoaderManager#initLoader initLoader()}), it checks to see whether
263the loader specified by the ID exists. If it doesn't, it triggers the {@link
264android.app.LoaderManager.LoaderCallbacks} method {@link
265android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. This
266is where you  create a new loader. Typically this will be a {@link
267android.content.CursorLoader}, but you can implement your own {@link
268android.content.Loader} subclass. </p>
269
270<p>In this example, the {@link
271android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
272callback method creates a {@link android.content.CursorLoader}. You must build
273the {@link android.content.CursorLoader} using its constructor method, which
274requires the complete set of information needed to perform a query to the {@link
275android.content.ContentProvider}. Specifically, it needs:</p>
276<ul>
277  <li><em>uri</em> &#8212; The URI for the content to retrieve. </li>
278  <li><em>projection</em> &#8212; A list of which columns to return. Passing
279<code>null</code> will return all columns, which is inefficient. </li>
280  <li><em>selection</em> &#8212; A filter declaring which rows to return,
281formatted as an SQL WHERE clause (excluding the WHERE itself). Passing
282<code>null</code> will return all rows for the given URI. </li>
283  <li><em>selectionArgs</em> &#8212; You may include ?s in the selection, which will
284be replaced by the values from <em>selectionArgs</em>, in the order that they appear in
285the selection. The values will be bound as Strings. </li>
286  <li><em>sortOrder</em> &#8212; How to order the rows, formatted as an SQL
287ORDER BY clause (excluding the ORDER BY itself). Passing <code>null</code> will
288use the default sort order, which may be unordered.</li>
289</ul>
290<p>For example:</p>
291<pre>
292 // If non-null, this is the current filter the user has provided.
293String mCurFilter;
294...
295public Loader&lt;Cursor&gt; onCreateLoader(int id, Bundle args) {
296    // This is called when a new Loader needs to be created.  This
297    // sample only has one Loader, so we don't care about the ID.
298    // First, pick the base URI to use depending on whether we are
299    // currently filtering.
300    Uri baseUri;
301    if (mCurFilter != null) {
302        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
303                  Uri.encode(mCurFilter));
304    } else {
305        baseUri = Contacts.CONTENT_URI;
306    }
307
308    // Now create and return a CursorLoader that will take care of
309    // creating a Cursor for the data being displayed.
310    String select = &quot;((&quot; + Contacts.DISPLAY_NAME + &quot; NOTNULL) AND (&quot;
311            + Contacts.HAS_PHONE_NUMBER + &quot;=1) AND (&quot;
312            + Contacts.DISPLAY_NAME + &quot; != '' ))&quot;;
313    return new CursorLoader(getActivity(), baseUri,
314            CONTACTS_SUMMARY_PROJECTION, select, null,
315            Contacts.DISPLAY_NAME + &quot; COLLATE LOCALIZED ASC&quot;);
316}</pre>
317<h4 id="onLoadFinished">onLoadFinished</h4>
318
319<p>This method is called when a previously created loader has finished its load.
320This method is guaranteed to be called prior to the release of  the last data
321that was supplied for this loader.  At this point  you should remove all use of
322the old data (since it will be released  soon), but should not do your own
323release of the data since its loader  owns it and will take care of that.</p>
324
325
326<p>The loader will release the data once it knows the application  is no longer
327using it.  For example, if the data is  a cursor from a {@link
328android.content.CursorLoader},  you should not call {@link
329android.database.Cursor#close close()} on it yourself. If the cursor is being
330placed in a {@link android.widget.CursorAdapter}, you should use the {@link
331android.widget.SimpleCursorAdapter#swapCursor swapCursor()}  method so that the
332old {@link android.database.Cursor} is not closed. For example:</p>
333
334<pre>
335// This is the Adapter being used to display the list's data.<br
336/>SimpleCursorAdapter mAdapter;
337...
338
339public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor data) {
340    // Swap the new cursor in.  (The framework will take care of closing the
341    // old cursor once we return.)
342    mAdapter.swapCursor(data);
343}</pre>
344
345<h4 id="onLoaderReset">onLoaderReset</h4>
346
347<p>This method is called when a previously created loader is being reset,  thus
348making its data unavailable. This callback lets you find  out when the data is
349about to be released so you can remove your  reference to it.  </p>
350<p>This implementation calls
351{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}
352with a value of <code>null</code>:</p>
353
354<pre>
355// This is the Adapter being used to display the list's data.
356SimpleCursorAdapter mAdapter;
357...
358
359public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
360    // This is called when the last Cursor provided to onLoadFinished()
361    // above is about to be closed.  We need to make sure we are no
362    // longer using it.
363    mAdapter.swapCursor(null);
364}</pre>
365
366
367<h2 id="example">Example</h2>
368
369<p>As an example, here is the full implementation of a {@link
370android.app.Fragment} that displays a {@link android.widget.ListView} containing
371the results of a query against the contacts content provider. It uses a {@link
372android.content.CursorLoader} to manage the query on the provider.</p>
373
374<p>For an application to access a user's contacts, as shown in this example, its
375manifest must include the permission
376{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.</p>
377
378<pre>
379public static class CursorLoaderListFragment extends ListFragment
380        implements OnQueryTextListener, LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
381
382    // This is the Adapter being used to display the list's data.
383    SimpleCursorAdapter mAdapter;
384
385    // If non-null, this is the current filter the user has provided.
386    String mCurFilter;
387
388    @Override public void onActivityCreated(Bundle savedInstanceState) {
389        super.onActivityCreated(savedInstanceState);
390
391        // Give some text to display if there is no data.  In a real
392        // application this would come from a resource.
393        setEmptyText(&quot;No phone numbers&quot;);
394
395        // We have a menu item to show in action bar.
396        setHasOptionsMenu(true);
397
398        // Create an empty adapter we will use to display the loaded data.
399        mAdapter = new SimpleCursorAdapter(getActivity(),
400                android.R.layout.simple_list_item_2, null,
401                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
402                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
403        setListAdapter(mAdapter);
404
405        // Prepare the loader.  Either re-connect with an existing one,
406        // or start a new one.
407        getLoaderManager().initLoader(0, null, this);
408    }
409
410    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
411        // Place an action bar item for searching.
412        MenuItem item = menu.add(&quot;Search&quot;);
413        item.setIcon(android.R.drawable.ic_menu_search);
414        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
415        SearchView sv = new SearchView(getActivity());
416        sv.setOnQueryTextListener(this);
417        item.setActionView(sv);
418    }
419
420    public boolean onQueryTextChange(String newText) {
421        // Called when the action bar search text has changed.  Update
422        // the search filter, and restart the loader to do a new query
423        // with this filter.
424        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
425        getLoaderManager().restartLoader(0, null, this);
426        return true;
427    }
428
429    @Override public boolean onQueryTextSubmit(String query) {
430        // Don't care about this.
431        return true;
432    }
433
434    @Override public void onListItemClick(ListView l, View v, int position, long id) {
435        // Insert desired behavior here.
436        Log.i(&quot;FragmentComplexList&quot;, &quot;Item clicked: &quot; + id);
437    }
438
439    // These are the Contacts rows that we will retrieve.
440    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
441        Contacts._ID,
442        Contacts.DISPLAY_NAME,
443        Contacts.CONTACT_STATUS,
444        Contacts.CONTACT_PRESENCE,
445        Contacts.PHOTO_ID,
446        Contacts.LOOKUP_KEY,
447    };
448    public Loader&lt;Cursor&gt; onCreateLoader(int id, Bundle args) {
449        // This is called when a new Loader needs to be created.  This
450        // sample only has one Loader, so we don't care about the ID.
451        // First, pick the base URI to use depending on whether we are
452        // currently filtering.
453        Uri baseUri;
454        if (mCurFilter != null) {
455            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
456                    Uri.encode(mCurFilter));
457        } else {
458            baseUri = Contacts.CONTENT_URI;
459        }
460
461        // Now create and return a CursorLoader that will take care of
462        // creating a Cursor for the data being displayed.
463        String select = &quot;((&quot; + Contacts.DISPLAY_NAME + &quot; NOTNULL) AND (&quot;
464                + Contacts.HAS_PHONE_NUMBER + &quot;=1) AND (&quot;
465                + Contacts.DISPLAY_NAME + &quot; != '' ))&quot;;
466        return new CursorLoader(getActivity(), baseUri,
467                CONTACTS_SUMMARY_PROJECTION, select, null,
468                Contacts.DISPLAY_NAME + &quot; COLLATE LOCALIZED ASC&quot;);
469    }
470
471    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor data) {
472        // Swap the new cursor in.  (The framework will take care of closing the
473        // old cursor once we return.)
474        mAdapter.swapCursor(data);
475    }
476
477    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
478        // This is called when the last Cursor provided to onLoadFinished()
479        // above is about to be closed.  We need to make sure we are no
480        // longer using it.
481        mAdapter.swapCursor(null);
482    }
483}</pre>
484<h3 id="more_examples">More Examples</h3>
485
486<p>There are a few different samples in <strong>ApiDemos</strong> that
487illustrate how to use loaders:</p>
488<ul>
489  <li><a
490href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">
491LoaderCursor</a> &#8212; A complete version of the
492snippet shown above.</li>
493  <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> &#8212; An example of how to use throttling to
494reduce the number of queries a content provider does then its data changes.</li>
495</ul>
496
497<p>For information on downloading and installing the SDK samples, see <a
498href="http://developer.android.com/resources/samples/get.html"> Getting the
499Samples</a>. </p>
500
501