Выбрать главу

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

  count = db.update(getTableName(), values, getIdColumnName() + "="

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

   whereArgs);

 }

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

 return count;

}

In this case, updates can either be to a specific instance or applied across the entire collection, so we check the Uri (isCollectionUri()) and, if it is an update for the collection, just perform the update. If we are updating a single instance, we need to add a constraint to the WHERE clause to only update for the requested row.

delete()

Like update(), delete() receives a Uri representing the instance or collection to work with and a WHERE clause and parameters. If the activity is deleting a single instance, the Uri should represent that instance and the WHERE clause may be null. But the activity might be requesting to delete an open-ended set of instances, using the WHERE clause to constrain which ones to delete.

As with update(), though, this is simple if you are using SQLite for database storage (sense a theme?). You can let it handle the idiosyncrasies of parsing and applying the WHERE clause — all you have to do is call delete() on the database.

For example, here is delete() from Provider:

@Override

public int delete(Uri url, String where, String[] whereArgs) {

 int count;

 long rowId = 0;

 if (isCollectionUri(url)) {

  count = db.delete(getTableName(), where, whereArgs);

 } else {

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

  rowId = Long.parseLong(segment);

  count = db.delete(getTableName(), getIdColumnName() + "="

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

   whereArgs);

 }

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

 return count;

}

This is almost a clone of the update() implementation described earlier in this chapter — either delete a subset of the entire collection or delete a single instance (if it also satisfies the supplied WHERE clause).

getType()

The last method you need to implement is getType(). This takes a Uri and returns the MIME type associated with that Uri. The Uri could be a collection or an instance Uri; you need to determine which was provided and return the corresponding MIME type.

For example, here is getType() from Provider:

@Override

public String getType(Uri url) {

 if (isCollectionUri(url)) {

  return(getCollectionType());

 }

 return(getSingleType());

}

As you can see, most of the logic delegates to private getCollectionType() and getSingleType() methods:

private String getCollectionType() {

 return("vnd.android.cursor.dir/vnd.commonsware.constant");

}

private String getSingleType() {

 return("vnd.android.cursor.item/vnd.commonsware.constant");

}

Step #2: Supply a Uri

You also need to add a public static member… somewhere, containing the Uri for each collection your content provider supports. Typically this is a public static final Uri put on the content-provider class itself:

public static final Uri CONTENT_URI =

 Uri.parse("content://com.commonsware.android.tourit.Provider/tours");

You may wish to use the same namespace for the content Uri that you use for your Java classes, to reduce the chance of collision with others.

Step #3: Declare the Properties

Remember those properties you referenced when you were using a content provider in the previous chapter? Well, you need to have those too for your own content provider.

Specifically, you want a public static class implementing BaseColumns that contains your property names, such as this example from Provider:

public static final class Constants implements BaseColumns {

 public static final Uri CONTENT_URI =

  Uri.parse("content://com.commonsware.android.constants.Provider/constants");

 public static final String DEFAULT_SORT_ORDER = "title";

 public static final String TITLE = "title";

 public static final String VALUE = "value";

}

If you are using SQLite as a data store, the values for the property name constants should be the corresponding column name in the table, so you can just pass the projection (array of properties) to SQLite on a query(), or pass the ContentValues on an insert() or update().

Note that nothing in here stipulates the types of the properties. They could be strings, integers, or whatever. The biggest limitation is what a Cursor can provide access to via its property getters. The fact that there is nothing in code that enforces type safety means you should document the property types well so people attempting to use your content provider know what they can expect.

Step #4: Update the Manifest

The glue tying the content-provider implementation to the rest of your application resides in your AndroidManifest.xml file. Simply add a provider element as a child of the <application> element:

<provider

 android:name=".Provider"

 android:authorities="com.commonsware.android.tourit.Provider" />

The android:name property is the name of the content-provider class, with a leading dot to indicate it is in the stock namespace for this application’s classes (just like you use with activities).