summaryrefslogtreecommitdiffstats
path: root/docs/html/guide/topics/fundamentals/loaders.jd
blob: d31f0905dfe7623f20247d5b7aa9f80e5e1788b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
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/FragmentListCursorLoader.html">FragmentListCursorLoader</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()}  &#8212;
Instantiate and return a new {@link android.content.Loader} for the given ID.
</li></ul>
<ul>
  <li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
&#8212; Called when a previously created loader has finished its load.
</li></ul>
<ul>
  <li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}  
    &#8212; 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> &#8212; The URI for the content to retrieve. </li>
  <li><em>projection</em> &#8212; A list of which columns to return. Passing
<code>null</code> will return all columns, which is inefficient. </li>
  <li><em>selection</em> &#8212; 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> &#8212; 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> &#8212; 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&lt;Cursor&gt; 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 = &quot;((&quot; + Contacts.DISPLAY_NAME + &quot; NOTNULL) AND (&quot;
            + Contacts.HAS_PHONE_NUMBER + &quot;=1) AND (&quot;
            + Contacts.DISPLAY_NAME + &quot; != '' ))&quot;;
    return new CursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION, select, null,
            Contacts.DISPLAY_NAME + &quot; COLLATE LOCALIZED ASC&quot;);
}</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&lt;Cursor&gt; 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&lt;Cursor&gt; 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&lt;Cursor&gt; {

    // 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(&quot;No phone numbers&quot;);

        // 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(&quot;Search&quot;);
        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(&quot;FragmentComplexList&quot;, &quot;Item clicked: &quot; + 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&lt;Cursor&gt; 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 = &quot;((&quot; + Contacts.DISPLAY_NAME + &quot; NOTNULL) AND (&quot;
                + Contacts.HAS_PHONE_NUMBER + &quot;=1) AND (&quot;
                + Contacts.DISPLAY_NAME + &quot; != '' ))&quot;;
        return new CursorLoader(getActivity(), baseUri,
                CONTACTS_SUMMARY_PROJECTION, select, null,
                Contacts.DISPLAY_NAME + &quot; COLLATE LOCALIZED ASC&quot;);
    }

    public void onLoadFinished(Loader&lt;Cursor&gt; 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&lt;Cursor&gt; 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/FragmentListCursorLoader.html">FragmentListCursorLoader</a> &#8212; 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> &#8212; An example of how to use throttling to
reduce the number of queries a content provider does then 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>