SQLite In Android Part 3: The ContentProvider

I’m glad you’ve made it to the third part of my segment on using SQLite in Android applications. Now that you know how to write your contract classes and create your database, you need to write the ContentProvider. As mentioned in the docs, the ContentProvider is only required if you want your application to be shared across applications.

Even though our movie database application won’t be sharing information with other apps, I’ve chosen to use a ContentProvider out of personal preference. If there is enough interest in the comments, I will write a follow up post on writing another type of data source.

Just as we did in the last two posts, let’s begin by discussing the class level variables of your ContentProvider.

Define the Available Uris

In our application, we will have two necessary Uris for each of our two tables:

  • A Uri used for querying the entire table.
  • A Uri used for querying a single row based on the identifier.

To signify these four Uris we have, we define static integers for each one at the class level:

You can use whatever numbers you see fit here, but I do recommend you try to implicitly group your Uris. For example, if I have four Uris based on genre, I’d give them 100, 101, 102, and 103, so that I can imply that any Uri that begins with a 1 is related to the genre table.

SQLiteOpenHelper and the UriMatcher

In order to read/write from your database, you’ll need to define an SQLiteOpenHelper object as we discussed in part two. You instantiate the object inside the onCreate() method:

In addition to this, you will need a UriMatcher object that is used to match a Uri to one of the integer representations defined earlier. This is done in our buildUriMatcher() method:

If you remember from the first post, we wrote a method called buildGenreUri() which used the ContentUris class to append an id to a path. The pound sign following the slash is what the UriMatcher uses to determine the string ends with a numeric variable. If you create a Uri using a query parameter that is a string, you can change this line to use an asterisk instead: PATH_GENRE + "/*".

Now that you have opened your database and written a UriMatcher to define all Uris used in this application, there are 5 required implementations for the ContentProvider.

getType

The purpose of this method is to determine the MIME type of the Uri it receives, which are the CONTENT_TYPE and CONTENT_ITEM_TYPE defined in our contract classes. We can write a switch statement that switches on the result of our UriMatcher, and return the necessary string. If we don’t get a valid match, or one for a Uri that we don’t support, we can just throw an exception:

Query

This function is implemented to handle requests to query the database. There are five parameters to this method:

  1. Uri: The table or row of a table being queried.
  2. Projection: A string array object for all of the columns to select from the table. Passing null will select all columns.
  3. Selection: A string representing a condition for the rows queried, such as only querying rows with a certain column value. To define any wildcards for this condition, you must use a question mark.
  4. SelectionArgs: A string array of arguments for the previous selection. The size of this array should be equal to the number of wildcards in the selection string. These arguments will replace the wildcards of the selection string in sequential order.
  5. SortOrder: A string of the column name and direction to sort the results by.

Not only do we return the Cursor for the query results, but we must also be sure to notify any observers of the specified Uri or its descendants:

For simplicity I have removed the MOVIE and MOVIE_ID case, since they will be identical to the others with the exception of the class and table names. I will do the same for the remaining portions as well.

Insert

This method inserts a row into a database table. To do this, you need a Uri for the table you are inserting into, and a ContentValues object which contains the column/value mappings for the row being inserted.

Because we insert rows into a table and expect the id to be automatically incremented, we do not need to consider the GENRE_ID and MOVIE_ID Uris here. If those are passed in, they will fall to the default case and an exception will be thrown.

If the insertion is successful, meaning a value greater than one is returned, we will return a Uri for that row. Otherwise, we’ll throw an exception that we were unable to insert:

Update and Delete

These two methods are used to update and delete rows from the database. Both require a Uri for the table to manipulate, and conditions for the rows being effected. The only difference is that update requires the ContentValues for the new column values for the rows being updated. Each method will return an integer value of the number of rows effected:

The AndroidManifest

Before you can use your ContentProvider, you need to include it inside the Application tag of your AndroidManifest, just like you would with an Activity:

Those are all of the required implementations for the ContentProvider! Don’t forget to add in the switch cases for the MOVIE Uri in the places where it has been left out. The entire file is on Github for your convenience.

At this point, you have everything you need for using SQLite in Android. However, if you want to feel confident in your use of the ContentProvider, be sure to read part 4 on how to test your ContentProvider.

Share this post...
Share on Reddit0Share on Google+0Tweet about this on TwitterShare on LinkedIn0Share on Facebook3

Leave a Reply