RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Using and Implementing Content Providers in Android : Page 2

Although Android ships with several useful content providers, you can easily extend it and build your own.


Build a Custom Content Provider

To get started, create a new Android project in Eclipse. Name the project "CPExamples" as shown in Figure 1.

Figure 1. New Android Project: This dialog lets you create a new Android project in Eclipse.

After Eclipse creates the project, add the code in Listing 1 to the CPExampleActivity class.

The code in Listing 1 accesses the call log of your Android device and prints out all the calls that you have made. The Activity class's managedQuery() method retrieves a managed cursor, which handles all the work of unloading itself when the application pauses, and re-querying itself when the application restarts. Using the Cursor object, you can iteratively access the results returned by the query.

For this program to work, you would also need to add in the READ_CONTACTS permission in the AndroidManifest.xml file:

   <?xml version="1.0" encoding="utf-8"?>
   <manifest xmlns:android=

     <application android:icon="@drawable/icon" 
       <activity android:name=".CPExampleActivity"
           <action android:name="android.intent.action.MAIN" />
           <category android:name=
             "android.intent.category.LAUNCHER" />
When you run the program, you should see something like this in the LogCat window—a list of all the calls made on the Android handset:

   02-23 01:41:15.130: VERBOSE/Content 
      Providers(774): 1, 654565677, Outgoing
   02-23 01:41:15.130: VERBOSE/Content 
      Providers(774): 2, 658373543, Outgoing
   02-23 01:41:15.140: VERBOSE/Content 
      Providers(774): 3, 653233350, Missed
   02-23 01:41:15.152: VERBOSE/Content 
      Providers(774): 4, 659888988, Incoming
Note that the managedQuery() method accepts five arguments:

   Cursor c = managedQuery(allCalls, null, null, null, null);        
The second argument to the managedQuery() method, known as "projections," controls how many columns the query returns. You can specify exactly which columns the method should return by creating an array containing the name of the desired columns. For example:

   String[] projection = new String[] {
      Calls._ID, Calls.NUMBER, Calls.TYPE};                
   Cursor c = managedQuery(allCalls, projection, 
      null, null, null);
The third and fourth parameters, known as "Filtering," let you specify a SQL WHERE clause to filter the query results. You can specify a filtering rule like this:

   Cursor c = managedQuery(allCalls, projection, 
      "Calls.NUMBER LIKE '65%'", //---retrieve numbers beginning with 65---
      null, null);
The fifth and last parameter lets you specify a SQL ORDER BY clause to sort the query results. This parameter is also known as "Sorting." You can specify a sorting rule like this:

   Cursor c = managedQuery(allCalls, projection, 
      "Calls.NUMBER LIKE '5%'", 
      "Calls.TYPE DESC"); //---sort result by call TYPE descending---       

Creating Your Own Content Providers

To create your own content provider in Android, all you need to do is to extend the abstract ContentProvider class and override the various methods defined within it.

Here's a simple content provider example that stores a list of books. For simplicity, the content provider stores the books in a table containing three fields as shown in Figure 2.

Figure 2. Content Provider Structure: The figures shows the structure of the content provider that you will be building.
Using the same package created in the previous section, add a new file to the src/ folder (src/net.learn2develop.CPExamples in this example) and name it BooksProvider.java.

Extend the ContentProvider class, import the various namespaces and implement the methods as shown in Listing 2.

The methods you need to implement are:

  • getType(): Returns the MIME type of the data at the given URI.
  • onCreate(): Called when the provider is being started.
  • query(): Receives a request from a client. The result is returned as a Cursor object.
  • insert(): Inserts a new record into the content provider.
  • delete(): Deletes an existing record from the content provider.
  • update(): Updates an existing record from the content provider.
Within your content provider, you may choose to store your data however you like: traditional file system, XML, database, or even through web services. This example uses the SQLite database approach discussed in the previous article.

Define the following constants within the BooksProvider class:

   public class BooksProvider extends ContentProvider 
      public static final String PROVIDER_NAME = 
      public static final Uri CONTENT_URI = 
         Uri.parse("content://"+ PROVIDER_NAME + "/books");
      public static final String _ID = "_id";
      public static final String TITLE = "title";
      public static final String ISBN = "isbn";
      private static final int BOOKS = 1;
      private static final int BOOK_ID = 2;   
      private static final UriMatcher uriMatcher;
         uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
         uriMatcher.addURI(PROVIDER_NAME, "books", BOOKS);
         uriMatcher.addURI(PROVIDER_NAME, "books/#", BOOK_ID);      
Observe from the code above that you use a UriMatcher object to parse the content URI that is passed to the content provider through a Content Resolver. For example, the following content URI represents a request for all books in the content provider:

In contrast, the following represents a request for a particular book with _id=5:

Define the code in Listing 3, so the content provider will use a SQLite database to store the books in the content provider. Note that you use the SQLiteOpenHelper helper class to help manage your database.

You'll need to override the getType() method so it uniquely describes the data type for your content provider. Using the UriMatcher object, you will return "vnd.android.cursor.item/vnd.learn2develop.books" for a single book, and "vnd.android.cursor.dir/vnd.learn2develop.books" for multiple books:

   public String getType(Uri uri) {
      switch (uriMatcher.match(uri)){
         //---get all books---
         case BOOKS:
            return "vnd.android.cursor.dir/vnd.learn2develop.books ";
         //---get a particular book---
         case BOOK_ID:                
            return "vnd.android.cursor.item/vnd.learn2develop.books ";
            throw new IllegalArgumentException("Unsupported URI: " + uri);        
Next, override the onCreate() method to open a connection to the database when the content provider is started:

   public boolean onCreate() {
      Context context = getContext();
      DatabaseHelper dbHelper = new DatabaseHelper(context);
      booksDB = dbHelper.getWritableDatabase();
      return (booksDB == null)? false:true;
To allow clients to query for books, override the query() method:

   public Cursor query(Uri uri, String[] projection, String selection,
      String[] selectionArgs, String sortOrder) {
      SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
      if (uriMatcher.match(uri) == BOOK_ID)
         //---if getting a particular book---
            _ID + " = " + uri.getPathSegments().get(1));                
      if (sortOrder==null || sortOrder=="")
         sortOrder = TITLE;
      Cursor c = sqlBuilder.query(
      //---register to watch a content URI for changes---
      c.setNotificationUri(getContext().getContentResolver(), uri);
      return c;
By default, the query result is sorted using the title field. The resulting query is returned as a Cursor object.

To allow new books to be inserted into the content provider, override the insert() method:

   public Uri insert(Uri uri, ContentValues values) {
      //---add a new book---
      long rowID = booksDB.insert(
         DATABASE_TABLE, "", values);
      //---if added successfully---
      if (rowID>0)
         Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
         getContext().getContentResolver().notifyChange(_uri, null);    
         return _uri;                
      throw new SQLException("Failed to insert row into " + uri);
After inserting the record successfully, call the Content Resolver's notifyChange() method to notify registered observers that a row was updated.

To delete a book, override the delete() method:

   public int delete(Uri arg0, String arg1, String[] arg2) {
      // arg0 = uri 
      // arg1 = selection
      // arg2 = selectionArgs
      int count=0;
      switch (uriMatcher.match(arg0)){
         case BOOKS:
            count = booksDB.delete(
         case BOOK_ID:
            String id = arg0.getPathSegments().get(1);
            count = booksDB.delete(
               _ID + " = " + id + 
               (!TextUtils.isEmpty(arg1) ? " AND (" + 
               arg1 + ')' : ""), 
         default: throw new IllegalArgumentException(
            "Unknown URI " + arg0);    
      getContext().getContentResolver().notifyChange(arg0, null);
      return count;      
You also need to call the Content Resolver's notifyChange()method after the deletion to notify registered observers that a row was deleted.

Finally, to update a book, override the update() method:

   public int update(Uri uri, ContentValues values, 
      String selection, String[] selectionArgs) 
      int count = 0;
      switch (uriMatcher.match(uri)){
         case BOOKS:
            count = booksDB.update(
         case BOOK_ID:                
            count = booksDB.update(
               _ID + " = " + uri.getPathSegments().get(1) + 
               (!TextUtils.isEmpty(selection) ? " AND (" + 
                  selection + ')' : ""), 
         default: throw new IllegalArgumentException(
            "Unknown URI " + uri);    
      getContext().getContentResolver().notifyChange(uri, null);
      return count;
Again, call notifyChange() to let registered observers know that a row was updated.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date