Android Tutorial: Using Content Providers

In this tutorial I am going to show you how to make use of existing content providers. It’s the second part of a three part tutorial on content providers. In the first part I covered the common concepts of content providers and in the next part I am going to deal with how to write your own content provider. The first part also lists all standard content providers that are part of the Android platform. If you haven’t read it yet you might want to do so now.

ContentResolver

Whenever you want to use another content provider you first have to access a ContentResolver object. This object is responsible for finding the correct content provider.

The ContentResolver decides which provider to use based on the authority part of the URI. As you will see in the third part of this mini-series a content provider must provide its authority within the manifest file. From these entries Android creates a mapping between the authorities and the corresponding ContentProvider implementations to use.

You always interact with the ContentResolver and never with ContentProvider objects themselves. This supports loose coupling and also guarantees the correct lifecycle of the content provider.

You can get the ContentResolver object by calling getContentResolver() on the Context object. The Context object should always be available since the Activity and Service classes inherit from Context and the other components also provide easy access to it.

Since you only use the class ContentResolver it has to provide all necessary CRUD-methods. Any arguments provided to the methods of the ContentResolver are passed on to the respective methods of the ContentProvider subclass.

The CRUD methods of the ContentResolver object
Method Usage
delete Deletes the object(s) for the URI provided. The URI can be item- or directory-based
insert Inserts one object. The URI must be directory-based
query Queries for all objects that fit the URI. The URI can be item- or directory-based
update Updates one or all object(s). The URI can be item- or directory-based

There are also two methods for applying multiple CRUD operations at once as shown in the next table. The more useful one is applyBatch() which I have explained in a blog post about ContentProvider batch operations.

ContentResolver methods for dealing with multiple content provider operations
Method Usage
applyBatch Allows you to execute a list of ContentProviderOperation objects. Each ContentProviderOperation object can take its own URI and type of operation
bulkInsert Allows you to insert an array of ContentValues for a directory-based URI. You can only specify one URI for all objects you want to add

Querying for data

Querying data is probably the operation you will use most often. That’s true for your own providers but also for standard providers which offer some very valuable information.

For this tutorial I am going to use the UserDictionary. That’s probably the easiest standard provider available and thus well suited for showing the concepts. But by default this provider has no entries, so you better add some words first. On Ice Cream Sandwich (Android 4.0) you can do so in the “Language & input” system settings. Just click on “Personal dictionaries” and add some words.

Language & input settings

Language & input settings


Adding a word to the dictionary

Adding a word to the dictionary

The arguments of the query method
Type Name Usage
URI uri The URI of the object(s) to access. This is the only argument that must not be null
String[] projection This String array indicates which columns/attributes of the objects you want to access
String selection With this argument you can determine which records to return
String[] selectionArgs The binding parameters to the previous selection argument
String sortOrder If the result should be ordered you must use this argument to determine the sort order

The return value of the query method is a Cursor object. The cursor is used to navigate between the rows of the result and to read the columns of the current row. Cursors are important resources that have to be closed whenever you have no more use for them – otherwise you keep valuable resources from being released.

The following code snippet shows how to make use of this provider. I use the CONTENT_URI of UserDictionary.Words to access the dictionary. For this example I am only interested in the IDs of the words and the words itself.

ContentResolver resolver = getContentResolver();
String[] projection = new String[]{BaseColumns._ID, UserDictionary.Words.WORD};
Cursor cursor = 
      resolver.query(UserDictionary.Words.CONTENT_URI, 
            projection, 
            null, 
            null, 
            null);
if (cursor.moveToFirst()) {
   do {
      long id = cursor.getLong(0);
      String word = cursor.getString(1);
      // do something meaningful
   } while (cursor.moveToNext());
}

There are a some more information you could read, like an id to identify the application, that added the words, or the locale of the words. For more details see the UserDictionary.Words documentation.

Inserting new records

Very often your app needs to insert data. For the built-in content providers of Android this could be because you want to add events to the Calendar provider, people to the Contacts provider, words to the UserDictionary provider and so on.

The correct content URI for inserts can only be a directory-based URI because only these represent a collection of related items.

The values to insert are specified using a ContentValues object.
This object is not much more than a collection of key/value pairs. Of course, the keys of your ContentValues object must match columns/attributes of the objects you want to update – otherwise you will get an exception. For all columns of the new object for which no key/value-pair is provided the default value is used – which most often is null.

The arguments of the insert method
Type Name Usage
URI uri The directory-based URI to which to add the object. This argument must not be null
ContentValues values The values for the object to add. This argument also must not be null

The following code snippet shows you how to add data using a content provider:

ContentValues values = new ContentValues();
values.put(Words.WORD, "Beeblebrox");
resolver.insert(UserDictionary.Words.CONTENT_URI, values);

If you want to add multiple records to the same URI you can use the bulkInsert() method. This method differs from the normal insert() method only in that it takes an array of ContentValue objects instead of just one ContentValues object. So for each record you want to add, there must be an entry within the ContentValues array. If you want to add to different URIs though – or if you want to mix insert, update and delete operations, you should use applyBatch().

Updating data

To update records you basically provide the URI, a ContentValues object and optionally also a where-clause and arguments for this where-clause.

The arguments of the update method
Type Name Usage
URI uri The URI of the object(s) to access. This argument must not be null
ContentValues values The values to substitute the current data with. This argument also must not be null
String selection With this argument you can determine which records to update
String[] selectionArgs The binding parameters to the previous selection argument

In the next snippet I am going to change the word I’ve just added in the previous section.

values.clear();
values.put(Words.WORD, "Zaphod");
Uri uri = ContentUris.withAppendedId(Words.CONTENT_URI, id);
long noUpdated = resolver.update(uri, values, null, null);

Here we use a ContentValues object again. The keys of your ContentValues object must of course match columns/attributes of the objects you want to update – otherwise you would get an exception. The update method changes only those columns for which keys are present in the ContentValues object.

Note the call to ContentUris.withAppendedId(). This is a helper method to create an id-based URI from a directory-based one. You use it all the time since content providers only provide constants for directory-based URIs. So whenever you want to access a specific object you should use this method.

Since I changed only one record, a URI with an appended ID is sufficient. But if you want to update multiple values, you should use the normal URI and a selection clause. You will see an example for the latter when I show you how to delete entries.

There is also the call to values.clear(). This resets the ContentValues object and thus recycles the object. This way you are reducing costly garbage collector operations.

Deleting data

The next snippet shows how to delete records. It finally deletes the word. In this code sample you can see how to use the selection and selectionArgs arguments. The array of the selectionArgs argument is used to substitute all question marks found in the selection argument.

long noDeleted = resolver.delete
      (Words.CONTENT_URI, 
      Words.WORD + " = ? ", 
      new String[]{"Zaphod"});

The delete method takes the same arguments as the update method with the exception being the values argument. Since the record is deleted anyway, substitute values are not needed.

The arguments of the delete method
Type Name Usage
URI uri The URI of the object(s) to access. This is the only argument which must not be null
String selection With this argument you can determine which records to delete
String[] selectionArgs The binding parameters to the previous selection argument

Share this article:

You can leave a response, or trackback from your own site.

20 Responses to “Android Tutorial: Using Content Providers”

  1. Chedli says:

    Hey, I like your website. It’s been very useful to me to understand some advanced android concepts such as Content Providers for instance.
    Just to say that this article is great and you got an <a> link that your forgot to close

    Keep updating this blog with new tutorials and tricks and best practices ;)

    • Thanx, Chedli! I’m glad that you like my posts. I definitely will keep updating and I already have tons of plans for coming posts.

      Which a-tag didn’t I close? On which page? And what was the text? I cannot find it.

      • Chedli says:

        In this tutorial, section : updating data and here’s a part of the text :
        Note the call to <a href=”http://developer.android.com/reference/android/content/ContentUris.html” title=”Documentation of the ContentUris class” rel=”nofollow”>ContentUris.withAppendedId(). This is a helper method to create an id-based URI from a directory-based one. You use it all the time since content providers only provide constants for directory-based URIs. So whenever you want to access a specific object you should use this method.

        Hope this would help you ;)

  2. TI says:

    THANKS VERY MUCH! YOU HAVE HELPED, WHERE GOOGLED SHOULD HAVE…

  3. Anonymous says:

    Thanks for putting up and maintaining this website. Be blessed

  4. Thanks for this excellent article !

  5. AnujD says:

    Thanks for this nice tut. I am working on app that retrives all Words in UserDictionary to later resore them. It is working fine on Default Android Keyboard in Jelly Bean. But It do not retrives any word fromn “Samsung Keyboard”

    I am using this code to get words:
    Cursor cursor = resolver.query(UserDictionary.Words.CONTENT_URI, projection, null, null, null);

    What is problem? Please help me… Its vey urgent and important. Thanks in advanced

    • Most likely Samsung isn’t using this provider. But I do not know this for a fact. Neither do I know if they use another content provider which is accessible by other apps.

      • AnujD says:

        Thanks for reply. I too feel the same. But can we do? From where can we get URI for Samsung? And is it like every keyboard app have there own uri to save words?
        Can u please help me regarding this? I searched a lot but coudnt find info on this.

        • If apps do not behave correctly one cannot do very much. It’s unfortunate if apps like Swype (that also doesn’t behave correctly) or Samsung’s keyboard do not use the UserDictionary. But unless they use a content provider, export its data and describe the structure of it somewhere, you cannot do anything. That’s why I can only urge app devs to actually use existing providers. And if there is a justifiable reason for not using an existing one, to export their data themself.

          You have to ask Samsung, Swype and any other offender how to access those data – if at all possible. I’m sorry, but I haven’t had a need for these, yet.

  6. Pushkar Tripathi says:

    This is great. It has been very helpful

  7. deepak says:

    YOU ARE GREATE IN CONTENT PROVIDER

  8. Jacob Wan says:

    This was very helpful, so thank you! There are a couple of trivial bugs in the cursor iteration snippet:

    – long id = cursor.getString(0));
    – String word = cursor.getString(1));
    + long id = cursor.getLong(0);
    + String word = cursor.getString(1);

  9. Kumar says:

    Hi can you please explain projection array importance in sqlite. Which is efficient give necessary values or null.

Leave a Reply

You can also subscribe without commenting.

Subscribe to RSS Feed My G+-Profile Follow me on Twitter!