Home | History | Annotate | Download | only in components
      1 page.title=Loaders
      2 parent.title=Activities
      3 parent.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
     35 href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">
     36 LoaderCursor</a></li>
     37      <li> <a
     38 href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">
     39 LoaderThrottle</a></li>
     40    </ol>
     41   </div>
     42 </div>
     43 
     44 <p>Introduced in Android 3.0, loaders make it easy to asynchronously load data
     45 in an activity or fragment. Loaders have these characteristics:</p>
     46   <ul>
     47     <li>They are available to every {@link android.app.Activity} and {@link
     48 android.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
     51 content changes.</li>
     52     <li>They automatically reconnect to the last loader's cursor when being
     53 recreated after a configuration change. Thus, they don't need to re-query their
     54 data.</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
     60 loaders 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
     71 android.content.Loader} instances.This helps an application manage
     72 longer-running operations in conjunction with the {@link android.app.Activity}
     73 or {@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
     75 their 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
     79 multiple 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
     84 android.app.LoaderManager}. For example, you use the {@link
     85 android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
     86 callback 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
     91 the base class for a loader. You would typically use {@link
     92 android.content.CursorLoader}, but you can implement your own subclass. While
     93 loaders are active they should monitor the source of their data and deliver new
     94 results 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
    104 android.database.Cursor}. This class implements the {@link
    105 android.content.Loader} protocol in a standard way for querying cursors,
    106 building on {@link android.content.AsyncTaskLoader} to perform the cursor query
    107 on a background thread so that it does not block the application's UI. Using
    108 this loader is the best way to asynchronously load data from a {@link
    109 android.content.ContentProvider}, instead of performing a managed query through
    110 the 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
    115 you'll use to implement a loader in your application. You won't need all of them
    116 for each loader you create, but you'll always need a reference to the {@link
    117 android.app.LoaderManager} in order to initialize a loader and an implementation
    118 of a {@link android.content.Loader} class such as {@link
    119 android.content.CursorLoader}. The following sections show you how to use these
    120 classes 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
    124 application 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
    129 android.content.ContentProvider}. Alternatively, you can implement your own subclass
    130 of {@link android.content.Loader} or {@link android.content.AsyncTaskLoader} to
    131 load data from some other source.</li>
    132   <li>An implementation for {@link android.app.LoaderManager.LoaderCallbacks}.
    133 This is where you create new loaders and manage your references to existing
    134 loaders.</li> 
    135 <li>A way of displaying the loader's data, such as a {@link
    136 android.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
    143 android.content.Loader} instances within an {@link android.app.Activity} or
    144 {@link android.app.Fragment}. There is only one {@link
    145 android.app.LoaderManager} per activity or fragment.</p> 
    146 
    147 <p>You typically
    148 initialize a {@link android.content.Loader} within the activity's {@link
    149 android.app.Activity#onCreate onCreate()} method, or within the fragment's
    150 {@link android.app.Fragment#onActivityCreated onActivityCreated()} method. You
    151 do this as follows:</p>
    152 
    153 <pre>// Prepare the loader.  Either re-connect with an existing one,
    154 // or start a new one.
    155 getLoaderManager().initLoader(0, null, this);</pre>
    156 
    157 <p>The {@link android.app.LoaderManager#initLoader initLoader()} method takes
    158 the 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
    162 construction (<code>null</code> in this example).</li> 
    163 
    164 <li>A {@link android.app.LoaderManager.LoaderCallbacks} implementation, which
    165 the {@link android.app.LoaderManager} calls to report loader events. In this
    166 example, the local class implements the {@link
    167 android.app.LoaderManager.LoaderCallbacks} interface, so it passes a reference
    168 to itself, {@code this}.</li> 
    169 </ul>
    170 <p>The {@link android.app.LoaderManager#initLoader initLoader()} call ensures that a loader
    171 is 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
    174 is 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()}.
    178 This is where you  implement the code to instantiate and return a new loader.
    179 For more discussion, see the section <a
    180 href="#onCreateLoader">onCreateLoader</a>.</li>
    181 </ul>
    182 <p>In either case, the given {@link android.app.LoaderManager.LoaderCallbacks}
    183 implementation is associated with the loader, and  will be called when the
    184 loader state changes.  If at the point of this call  the caller is in its
    185 started state, and the requested loader already exists and has generated its
    186 data, then the system calls {@link
    187 android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
    188 immediately (during {@link android.app.LoaderManager#initLoader initLoader()}),
    189 so you must be prepared for this to happen. See <a href="#onLoadFinished">
    190 onLoadFinished</a> for more discussion of this callback</p>
    191 
    192 <p>Note that the {@link android.app.LoaderManager#initLoader initLoader()}
    193 method returns the {@link android.content.Loader} that is created, but you don't
    194 need to capture a reference to it. The {@link android.app.LoaderManager} manages
    195 the life of the loader automatically. The {@link android.app.LoaderManager}
    196 starts and stops loading when necessary, and maintains the state of the loader
    197 and its associated content. As this implies, you rarely interact with loaders
    198 directly (though for an example of using loader methods to fine-tune a loader's
    199 behavior, see the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> sample). 
    200 You most commonly use the {@link
    201 android.app.LoaderManager.LoaderCallbacks} methods to intervene in the loading
    202 process when particular events occur. For more discussion of this topic, see <a
    203 href="#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
    208 shown above, it uses an existing loader with the specified ID if there is one.
    209 If there isn't, it creates one. But sometimes you want to discard your old data
    210 and start over.</p>
    211 
    212 <p>To discard your old data, you use {@link
    213 android.app.LoaderManager#restartLoader restartLoader()}. For example, this
    214 implementation of {@link android.widget.SearchView.OnQueryTextListener} restarts
    215 the loader when the user's query changes. The loader needs to be restarted so
    216 that it can use the revised search filter to do a new query:</p>
    217 
    218 <pre>
    219 public 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
    231 that lets a client  interact with the {@link android.app.LoaderManager}. </p>
    232 <p>Loaders, in particular {@link android.content.CursorLoader}, are  expected to
    233 retain their  data after being stopped. This allows applications to keep their
    234 data across the activity or fragment's {@link android.app.Activity#onStop
    235 onStop()} and {@link android.app.Activity#onStart onStart()} methods, so that
    236 when users return to an application, they don't have to wait for the data to
    237 reload. You use the {@link android.app.LoaderManager.LoaderCallbacks} methods
    238 when 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
    242 methods:</p>
    243 <ul>
    244   <li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}  &#8212;
    245 Instantiate 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
    254 data 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
    262 android.app.LoaderManager#initLoader initLoader()}), it checks to see whether
    263 the loader specified by the ID exists. If it doesn't, it triggers the {@link
    264 android.app.LoaderManager.LoaderCallbacks} method {@link
    265 android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. This
    266 is where you  create a new loader. Typically this will be a {@link
    267 android.content.CursorLoader}, but you can implement your own {@link
    268 android.content.Loader} subclass. </p>
    269 
    270 <p>In this example, the {@link
    271 android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}
    272 callback method creates a {@link android.content.CursorLoader}. You must build
    273 the {@link android.content.CursorLoader} using its constructor method, which
    274 requires the complete set of information needed to perform a query to the {@link
    275 android.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,
    281 formatted 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
    284 be replaced by the values from <em>selectionArgs</em>, in the order that they appear in
    285 the selection. The values will be bound as Strings. </li>
    286   <li><em>sortOrder</em> &#8212; How to order the rows, formatted as an SQL
    287 ORDER BY clause (excluding the ORDER BY itself). Passing <code>null</code> will
    288 use 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.
    293 String mCurFilter;
    294 ...
    295 public 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.
    320 This method is guaranteed to be called prior to the release of  the last data
    321 that was supplied for this loader.  At this point  you should remove all use of
    322 the old data (since it will be released  soon), but should not do your own
    323 release 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
    327 using it.  For example, if the data is  a cursor from a {@link
    328 android.content.CursorLoader},  you should not call {@link
    329 android.database.Cursor#close close()} on it yourself. If the cursor is being
    330 placed in a {@link android.widget.CursorAdapter}, you should use the {@link
    331 android.widget.SimpleCursorAdapter#swapCursor swapCursor()}  method so that the
    332 old {@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 
    339 public 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 
    348 making its data unavailable. This callback lets you find  out when the data is
    349 about to be released so you can remove your  reference to it. </p>
    350 <p>This implementation calls 
    351 {@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}  
    352 with a value of <code>null</code>:</p>
    353 
    354 <pre>
    355 // This is the Adapter being used to display the list's data.
    356 SimpleCursorAdapter mAdapter;
    357 ...
    358 
    359 public 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
    370 android.app.Fragment} that displays a {@link android.widget.ListView} containing
    371 the results of a query against the contacts content provider. It uses a {@link
    372 android.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
    375 manifest must include the permission
    376 {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.</p>
    377 
    378 <pre>
    379 public 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
    487 illustrate how to use loaders:</p>
    488 <ul>
    489   <li><a
    490 href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">
    491 LoaderCursor</a> &#8212; A complete version of the
    492 snippet 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
    494 reduce the number of queries a content provider does when its data changes.</li>
    495 </ul>
    496 
    497 <p>For information on downloading and installing the SDK samples, see <a
    498 href="http://developer.android.com/resources/samples/get.html"> Getting the
    499 Samples</a>. </p>
    500 
    501