Creating Your Content Provider

Using Eclipse, open FriendsProvider.java, which will become the Content Provider for your project. You are going to use this custom Content Provider in your Activity to retrieve data from your Friends database.

As always, let's start by looking at the imports for this file. You need to import the Friends class and several other classes:

import android_programmers_guide.FindAFriend.Friends;

import android.content.*;

import android.database.Cursor;

import android.database.SQLException;

import android.database.sqlite.SQLiteOpenHelper;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteQueryBuilder;

import android.net.Uri;

import android.text.TextUtils;

import android.util.Log;

import java.util.HashMap;

As you can see, you are importing several packages here, most of which deal with SQL. I will explain these packages as you use them.

The package you will be using first is android.content. To utilize and override the required methods for being a Content Provider, your FriendsProvider class needs to extend ContentProvider. Take a look at the following class outline, which includes several variable definitions that you will use throughout your provider:

public class FriendsProvider extends ContentProvider { private SQLiteDatabase mDB;

private static final String TAG = "FriendsProvider"; private static final String DATABASE_NAME = "friends"; private static final int DATABASE_VERSION = 2;

private static HashMap<String, String> FRIENDS_PROJECTION_MAP;

private static final int FRIENDS = 1; private static final int FRIENDS_ID = 2; private static final UriMatcher URL_MATCHER;}

The Content Provider contains several methods that you will want to override, including onCreate( ), query( ), insert( ), delete( ), and update( ). Because these methods will be called by Activities using your Content Provider, you must override them to specifically access the Friends database.

The onCreate( ) method that you will be overriding calls a SQLiteOpenHelper. Therefore, before you can override the onCreate( ) method of the ContentProvider, you have to create a class that extends SQLiteOpenHelper.

The code block that follows is a subclass of your Content Provider that extends SQLiteOpenHelper:

private static class DatabaseHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE friends (_id INTEGER PRIMARY KEY,"

+ "name TEXT," + "location TEXT," + "created INTEGER," + "modified INTEGER" + ");");

@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

Log.w(TAG, "Upgrading database from version " + oldVersion + "to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS friends"); onCreate(db);

The DatabaseHelper class you just created contains two overridden methods: onCreate( ) and onUpgrade( ). The onCreate( ) method is used when creating the database from code, or in instances where the table definition does not exist.

NOTE

Given that you created the database structure from the adb shell, you will not rely on the onCreate( ) method of DatabaseHelper to establish your database.

With the DatabaseHelper class created, you can now override the onCreate( ) method for your Content Provider:

@Override public boolean onCreate() {

DatabaseHelper dbHelper = new DatabaseHelper(); mDB = dbHelper.openDatabase(getContext(), DATABASE_NAME, null, DATABASE_VERSION);

This is a fairly simple method that, in the end, returns a Boolean representing whether or not your database could be opened. You use the SQLiteOpenHelper created in your sibling class to open the Friends database. Notice that you pass the database name into the DatabaseHelper class. If the database object—mDB—is not null when it returns, then the database was successfully opened and you can query it.

Next, override the query( ) method of the ContentProvider class. This will be the meat of your Content Provider. The query( ) method is called from your Activity through the Content Provider to gather the records from your database. Take a look at the code in the overridden version of the query( ) method:

@Override public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort) {

SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

switch (URL_MATCHER.match(url)) {

case FRIENDS:

qb.setTables("friends");

qb.setProjectionMap(FRIENDS_PROJECTION_MAP); break;

case FRIENDS_ID:

qb.setTables("friends");

qb.appendWhere("_id=" + url.getPathSegments().get(1)); break;

default:

throw new IllegalArgumentException("Unknown URL " + url);

String orderBy;

if (TextUtils.isEmpty(sort)) {

orderBy = Friends.Friend.DEFAULT_SORT_ORDER;

orderBy = sort;

Cursor c = qb.query(mDB, projection, selection, selectionArgs, null, null, orderBy);

c.setNotificationUri(getContext().getContentResolver(), url);

return c;

The query( ) method does a little bit of housekeeping, by checking the validity of the database URL passed into it and defining a query sort order. The URL check is to ensure that you are trying to access only the Friends database. If you are attempting to access a database from another Activity, or from another Content Provider, the query( ) method throws an exception.

Toward the end of the method, you perform a query using a SQLiteQueryBuilder. The resulting dataset is assigned to a Cursor using the following line of code:

Cursor c = qb.query(mDB, projection, selection, selectionArgs, null, null, orderBy);

NOTE

A Cursor is a device that allows you to move through records and return information from columns.

The update( ), delete( ), and insert( ) methods are also fairly straightforward in design. Take a look at these three methods, as you should override them:

@Override public Uri insert(Uri url, ContentValues initialValues) { long rowID; ContentValues values; if (initialValues != null) {

values = new ContentValues(initialValues); } else {

values = new ContentValues();

throw new IllegalArgumentException("Unknown URL " + url);

Long now = Long.valueOf(System.currentTimeMillis()); Resources r = Resources.getSystem();

if (values.containsKey(Friends.Friend.CREATED_DATE ) == false) { values.put(Friends.Friend.CREATED_DATE, now);

if (values.containsKey(Friends.Friend.MODIFIED_DATE) == false) { values.put(Friends.Friend.MODIFIED_DATE, now);

if (values.containsKey(Friends.Friend.NAME) == false) { values.put(Friends.Friend.NAME, r.getString(android.R.string.untitled)); }

if (values.containsKey(Friends.Friend.LOCATION) == false) { values.put(Friends.Friend.LOCATION , "");

rowlD = mDB.insert("friends", "friend", values); if (rowlD > 0) {

Uri uri = ContentUris.withAppendedId(Friends.Friend.CONTENT_URI

getContext().getContentResolver().notifyChange(uri, null); return uri;

throw new SQLException("Failed to insert row into " + url);

@Override public int delete(Uri url, String where, String[] whereArgs) { int count; long rowld = 0;

switch (URL_MATCHER.match(url)) { case FRIENDS:

count = mDB.delete("friends", where, whereArgs); break;

case FRIENDS_ID:

String segment = url.getPathSegments().get(1); rowId = Long.parseLong(segment); count = mDB

.delete("friends", "_id=" + segment

+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);

break; default:

throw new IllegalArgumentException("Unknown URL " + url);

getContext().getContentResolver().notifyChange(url, null); return count;

@Override public int update(Uri url, ContentValues values, String where, String[] whereArgs) {

int count;

switch (URL_MATCHER.match(url)) { case FRIENDS:

count = mDB.update("friends", values, where, whereArgs); break;

case FRIENDS_ID:

String segment = url.getPathSegments().get(1); count = mDB

.update("friends", values, "_id=" + segment

+ (ITextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break;

default:

throw new IllegalArgumentException("Unknown URL " + url);

getContext().getContentResolver().notifyChange(url, null); return count;

The code within these methods should be fairly self-explanatory. If you look past the housekeeping that takes place in each method, the core of the code issues a database statement to perform the requested action of updating, deleting, or inserting.

The final part of the Content Provider will be a getType( ) method that returns the type of your Friends data. When creating your own type, you should always follow this convention:

vnd.android.cursor.dir/vnd.<package>

Take a look at the getType( ) method:

@Override public String getType(Uri url) {

switch (URL_MATCHER.match(url)) { case FRIENDS: return

"vnd.android.cursor.dir/vnd.android_programmers_guide.friend";

case FRIENDS_ID: return

"vnd.android.cursor.item/vnd.android_programmers_guide.friend"; default:

throw new IllegalArgumentException("Unknown URL " + url);

That should complete your new custom Content Provider. Take a look at the completed FriendsProvider code:

package android_programmers_guide.FindAFriend; import android_programmers_guide.FindAFriend.Friends; import android.content.*;

import android.database.Cursor;

import android.database.SQLException;

import android.database.sqlite.SQLiteOpenHelper;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteQueryBuilder;

import android.net.Uri;

import android.text.TextUtils;

import android.util.Log;

import java.util.HashMap;

public class FriendsProvider extends ContentProvider {

private SQLiteDatabase mDB;

private static final String TAG = "FriendsProvider";

private static final String DATABASE_NAME = "friends";

private static final int DATABASE_VERSION = 2;

private static HashMap<String, String> FRIENDS_PROJECTION_MAP;

private static final int FRIENDS = 1;

private static final int FRIENDS_ID = 2;

private static final UriMatcher URL_MATCHER;

private static class DatabaseHelper extends SQLiteOpenHelper {

@Override public void onCreate(SQLiteDatabase db) {

db.execSQL("CREATE TABLE friends (_id INTEGER PRIMARY KEY,"

+ "name TEXT," + "location TEXT," + "created INTEGER, + "modified INTEGER" + ");");

@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

Log.w(TAG, "Upgrading database from version " + oldVersion + "to "

+ newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS friends"); onCreate(db);

@Override public boolean onCreate() {

DatabaseHelper dbHelper = new DatabaseHelper();

mDB = dbHelper.openDatabase(getContext(), DATABASE_NAME, null, DATABASE_VERSION);

@Override public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

switch (URL_MATCHER.match(url)) { case FRIENDS:

qb.setTables("friends");

qb.setProjectionMap(FRIENDS_PROJECTION_MAP); break;

case FRIENDS_ID:

qb.setTables("friends");

qb.appendWhere("_id=" + url.getPathSegments().get(1)); break;

default:

throw new IllegalArgumentException("Unknown URL " + url)

String orderBy;

if (TextUtils.isEmpty(sort)) {

orderBy = Friends.Friend.DEFAULT_SORT_ORDER; } else {

orderBy = sort;

Cursor c = qb.query(mDB, projection, selection, selectionArgs, null, null, orderBy);

c.setNotificationUri(getContext().getContentResolver(), url); return c;

@Override public String getType(Uri url) {

switch (URL_MATCHER.match(url)) { case FRIENDS: return

"vnd.android.cursor.dir/vnd.android_programmers_guide.friend";

case FRIENDS_ID: return

"vnd.android.cursor.item/vnd.android_programmers_guide.friend";

default:

throw new IllegalArgumentException("Unknown URL " + url)

@Override public Uri insert(Uri url, ContentValues initialValues) { long rowID; ContentValues values; if (initialValues != null) {

values = new ContentValues(initialValues); } else {

values = new ContentValues();

throw new IllegalArgumentException("Unknown URL " + url);

Long now = Long.valueOf(System.currentTimeMillis()); Resources r = Resources.getSystem();

if (values.containsKey(Friends.Friend.CREATED_DATE ) == false) { values.put(Friends.Friend.CREATED_DATE, now);

if (values.containsKey(Friends.Friend.MODIFIED_DATE) == false) { values.put(Friends.Friend.MODIFIED_DATE, now);

if (values.containsKey(Friends.Friend.NAME) == false) { values.put(Friends.Friend.NAME, r.getString(android.R.string.untitled)); }

if (values.containsKey(Friends.Friend.LOCATION) == false) { values.put(Friends.Friend.LOCATION , "");

rowID = mDB.insert("friends", "friend", values); if (rowID > 0) {

Uri uri = ContentUris.withAppendedId(Friends.Friend.CONTENT_URI

getContext().getContentResolver().notifyChange(uri, null); return uri;

throw new SQLException("Failed to insert row into " + url);

@Override public int delete(Uri url, String where, String[] whereArgs) { int count; long rowId = 0;

switch (URL_MATCHER.match(url)) { case FRIENDS:

count = mDB.delete("friends", where, whereArgs); break;

case FRIENDS_ID:

String segment = url.getPathSegments().get(1); rowId = Long.parseLong(segment); count = mDB

.delete("friends", "_id=" + segment

+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);

break; default:

throw new IllegalArgumentException("Unknown URL " + url);

getContext().getContentResolver().notifyChange(url, null); return count;

@Override public int update(Uri url, ContentValues values, String where, String[] whereArgs) {

int count;

switch (URL_MATCHER.match(url)) { case FRIENDS:

count = mDB.update("friends", values, where, whereArgs); break;

case FRIENDS_ID:

String segment = url.getPathSegments().get(1); count = mDB

.update("friends", values, "_id=" + segment + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break;

default:

throw new IllegalArgumentException("Unknown URL " + url);

getContext().getContentResolver().notifyChange(url, null); return count;

static {

URL_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);

URL_MATCHER.addURI("android_programmers_guide.FindAFriend.Friends", "friend", FRIENDS);

URL_MATCHER.addURI("android_programmers_guide.FindAFriend.Friends", "friend/#", FRIENDS_ID);

FRIENDS_PROJECTION_MAP = new HashMap<String, String>(); FRIENDS_PROJECTION_MAP.put(Friends.Friend._ID, "_id"); FRIENDS_PROJECTION_MAP.put(Friends.Friend.NAME, "name"); FRIENDS_PROJECTION_MAP.put(Friends.Friend.LOCATION, "location"); FRIENDS_PROJECTION_MAP.put(Friends.Friend.CREATED_DATE, "created"); FRIENDS_PROJECTION_MAP.put(Friends.Friend.MODIFIED_DATE,

With the underlying data elements now created (the database, definitions, and Content Provider), you can begin to build the surrounding Activity. Remember, this activity will use the data in your database, display it to a list, and then allow the user to launch another Activity that places database items on a Google Maps Overlay. In the following section, you will build both Activities and complete your FindAFriend application.

0 0

Post a comment