page.title=Loaders parent.title=Activities parent.link=activities.html @jd:body

In this document

  1. Loader API Summary
  2. Using Loaders in an Application
    1. Starting a Loader
    2. Restarting a Loader
    3. Using the LoaderManager Callbacks
  3. Example
    1. More Examples

Key classes

  1. {@link android.app.LoaderManager}
  2. {@link android.content.Loader}

Related samples

  1. FragmentListCursorLoader
  2. LoaderThrottle

Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:

Loader API Summary

There are multiple classes and interfaces that may be involved in using loaders in an application. They are summarized in this table:

Class/Interface Description
{@link android.app.LoaderManager} 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.

There is only one {@link android.app.LoaderManager} per activity or fragment. But a {@link android.app.LoaderManager} can have multiple loaders.
{@link android.app.LoaderManager.LoaderCallbacks} 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.
{@link android.content.Loader} 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.
{@link android.content.AsyncTaskLoader} Abstract loader that provides an {@link android.os.AsyncTask} to do the work.
{@link android.content.CursorLoader} 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.

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.

Using Loaders in an Application

This section describes how to use loaders in an Android application. An application that uses loaders typically includes the following:

Starting a Loader

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.

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:

// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);

The {@link android.app.LoaderManager#initLoader initLoader()} method takes the following parameters:

The {@link android.app.LoaderManager#initLoader initLoader()} call ensures that a loader is initialized and active. It has two possible outcomes:

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 onLoadFinished for more discussion of this callback

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 LoaderThrottle 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 Using the LoaderManager Callbacks.

Restarting a Loader

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.

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:

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;
}

Using the LoaderManager Callbacks

{@link android.app.LoaderManager.LoaderCallbacks} is a callback interface that lets a client interact with the {@link android.app.LoaderManager}.

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.

{@link android.app.LoaderManager.LoaderCallbacks} includes these methods:

These methods are described in more detail in the following sections.

onCreateLoader

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.

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:

For example:

 // 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");
}

onLoadFinished

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.

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:

// This is the Adapter being used to display the list's data.
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); }

onLoaderReset

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.  

This implementation calls {@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()} with a value of null:

// 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);
}

Example

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.

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}.

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);
    }
}

More Examples

There are a few different samples in ApiDemos that illustrate how to use loaders:

For information on downloading and installing the SDK samples, see Getting the Samples.