Paul Brower bio photo

Paul Brower

Android development for fun and profit.

Email Twitter Github Stackoverflow Codementor

Find further code and implementation here: https://github.com/browep/AndroidCursorLoaderTutorial

First, why use a Cursor Loader? Why is it better than the old way with the Activity.managedQuery call? The answer comes down to threading. The Cursor Loader class allows for the querying and filling of a Cursor do be done in a background thread. This frees up the UI thread to handle user interactions.

Side note: If you are attempting to do anything in Android beyond a simple “Hello, World” app then you need to read this article on threading, don’t worry it’s not that long.

This tutorial is going to give you two examples of how to use the CursorLoader, one super-simple barebones example and one that incorporates a ListView to display the contents. But first:

The ContentProvider Setup

Let’s start with the simplest example of a CursorLoader working with a ContentProvider. In the attached project there is a ContentProvider that will supply a list of dog breeds. And in order to emulate what a slow ContentProvider would look like it will Thread.sleep for 3 seconds.

SlowContentProvider.java

public class SlowContentProvider extends ContentProvider {

  public static final String TAG = SlowContentProvider.class.getSimpleName();

  @Override
  public boolean onCreate() {
    Log.i(TAG,"onCreate");
    return true;
  }

  @Override
  public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
    try {
      Log.i(TAG,"sleeping");
      Thread.sleep(3000);

      MatrixCursor cursor = new MatrixCursor(new String[]{"_id","col1"});
      for(String name : new String[]{"poodle","labrador","german shephard","boston terrier","hound"}){
        cursor.addRow(new Object[]{0,name});
      }
      Log.i(TAG,"returning " + cursor);
      return cursor;

      } catch (InterruptedException e) {
        Log.e(TAG,"error sleeping",e);
        return null;

      }
    }

    @Override
    public String getType(Uri uri) {
      return null;  
    }

    @Override
    public Uri insert(Uri uri, ContentValues contentValues) {
      return null;  
    }

    @Override
    public int delete(Uri uri, String s, String[] strings) {
      return 0;  
    }

    @Override
    public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
      return 0;  
    }
  }

This query method makes a MatrixCursor out of a static list after sleeping but it could be querying from a database, pulling from an API over the web, or pulling from a different ContentProvider. The beauty of the ContentProvider contract is that you don’t have to care about the “behind-the-scenes” stuff.

You also need to register this ContentProvider in your AndroidManifest.xml in order for it to be recognized, see https://github.com/browep/AndroidCursorLoaderTutorial/blob/master/AndroidManifest.xml#L24

A Very Simple Cursor Loader

Let’s see if we can get the CursorLoader created with the minimal of fuss. This class will create a CursorLoader that will put its results into a TextView.

SimpleCursorLoader.java

import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.Html;
import android.widget.TextView;

public class SimpleCursorLoader extends FragmentActivity implements
LoaderManager.LoaderCallbacks<Cursor> {

  public static final String TAG = SimpleCursorLoader.class.getSimpleName();
  private static final int LOADER_ID = 0x01;
  private TextView textView;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_cursor_loader);
    textView = (TextView) findViewById(R.id.text_view);
    getSupportLoaderManager().initLoader(LOADER_ID, null, this);

  }

  public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {

    return new CursorLoader(this,
      Uri.parse("content://com.github.browep.cursorloader.data")
      , new String[]{"col1"}, null, null, null);
    }

    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
      cursor.moveToFirst();
      String text = (String) textView.getText();
      while (!cursor.isAfterLast()) {
        text += "<br />" + cursor.getString(1);
        cursor.moveToNext();
      }
      textView.setText(Html.fromHtml(text) );

    }

    public void onLoaderReset(Loader<Cursor> cursorLoader) {

    }
  }

A Note about the CursorLoader

The CursorLoader library is not available in the core Android SDK before API level 11 ( Honeycomb ). Fortunately, the Android team made it available through the Compatibility package included in the SDK tools. See here for information on obtaining the support library. You’ll see that in the above class the import statements reference the android.support.v4.content package which is included in the support library jar instead of the android.content package which would have the same classes but only for API level 11 and above.

The above FragmentActivity (also in the support library ) implements the LoaderManager.LoaderCallbacks interface which will have the methods for interacting with the Cursor. The _onCreate_ method has the line

getSupportLoaderManager().initLoader(LOADER_ID, null, this);

Here we are initializing the Loader using the LoaderMangager, passing it a an id ( can be any int that you want), a Bundle ( which will be passed to the LoaderManager.LoaderCallbacks.onCreateLoader method), and then we are identifying this Activity as the class that implements the callbacks.

Below that we are implementing the LoaderCallbacks. The important one for this example is the onCreateLoader method. In it we need to return a Loader that will describe the query we are trying to run. It needs the URI for the content provider as well as the arguments to construct the query: projection, selection, selection args, sort order.

When it’s time to load the cursor, the onCreateLoader method will be called. The CursorLoader that is returned will be used to construct the query, interact with the ContentProvider on a background thread and then return the filled cursor to onLoadFinished

In the above example we see the onLoadFinished just fills up a TextView with the cursor content. Let’s try a more interesting example.

Integrating a CursorLoader and a ListView

Although the above example is nice, it’s not very interesting. Let’s use it with something that’s more realistic like a ListView. The below Activity is very similar to the previous Activity but we have added a ListView to be populated with the Cursor contents.

ListViewCursorLoader.java

public class ListViewCursorLoader extends FragmentActivity implements
  LoaderManager.LoaderCallbacks&lt;Cursor&gt; {

    private static final int LOADER_ID = 0x02;
    private CursorAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setContentView(R.layout.listview_cursor_adapter);
      getSupportLoaderManager().initLoader(LOADER_ID, null, this);

      adapter = new SimpleCursorAdapter(this, R.layout.listview_item_layout, null,
        new String[]{&quot;col1&quot;}, new int[]{R.id.text_view},
        Adapter.NO_SELECTION);
        ListView listView = (ListView) findViewById(R.id.list);
        listView.setAdapter(adapter);

      }

      public Loader&lt;Cursor&gt; onCreateLoader(int i, Bundle bundle) {

        return new CursorLoader(this,
          Uri.parse(&quot;content://com.github.browep.cursorloader.data&quot;)
          , new String[]{&quot;col1&quot;}, null, null, null);
        }

        public void onLoadFinished(Loader&lt;Cursor&gt; cursorLoader, Cursor cursor) {
          adapter.swapCursor(cursor);
        }

        public void onLoaderReset(Loader&lt;Cursor&gt; cursorLoader) {
          adapter.swapCursor(null);
        }
        }

Here again we are init’ing the Loader in the onCreate method just as we did in the previous example. We are also creating a CursorAdapter to translate the Cursor content to a ListView. The SimpleCursorAdapter makes it simple to map the Cursor content to a View value. It does it by taking as arguments:

  • A layout file to inflate
  • A Cursor with the contents, which we are leaving as null for now.
  • A list of columns who’s values we are going to grab
  • A list of View id’s in the inflated layout to apply said column values Our example is simple enough that we just have one column to apply to one TextView in the inflated layout.

The final argument in the SimpleCursorAdapter constructor is a flag for behavior. This example has Adapter.NO_SELECTION. This mean the Cursor will NOT re-query if the content is changed. You do not want CursorAdapter.FLAG_AUTO_REQUERY as this will make the Cursor requery using the UI thread which is what we avoiding by using a CursorLoader in the first place. The CursorLoader will handle any data changes and then call onLoadFinished so that everything happens in a background thread.

Our onCreateLoader method stays the same but our onLoadFinished is a little different. Since we are being given a Cursor we can give it to the Adapter we created and it should handle the rest.

To round it out we implement onLoaderReset which will be called if the Loader’s data is reset/unavailable/invalid.

So there you go, working with CursorLoader is easy and you will see the advantages in the more responsive UI. All code for this tutorial including needed jars and a full Android project can be found in the Github repo at the top of the tutorial.