returncount;
}
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.
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
publicint 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);
returncount;
}
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).
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
publicString 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:
privateString getCollectionType() {
return("vnd.android.cursor.dir/vnd.commonsware.constant");
}
privateString getSingleType() {
return("vnd.android.cursor.item/vnd.commonsware.constant");
}
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 finalUri 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 classConstants implementsBaseColumns {
public static finalUri CONTENT_URI =
Uri. parse("content://com.commonsware.android.constants.Provider/constants");
public static finalString DEFAULT_SORT_ORDER = "title";
public static finalString TITLE = "title";
public static finalString 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 element:
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).
The android:authorities
property should be a semicolon-delimited list of the authority values supported by the content provider. Recall, from earlier in this chapter, that each content Uri
is made up of a scheme, an authority, a data type path, and an instance identifier. Each authority from each CONTENT_URI
value should be included in the android:authorities
list.
Now when Android encounters a content Uri
, it can sift through the providers registered through manifests to find a matching authority. That tells Android which application and class implements the content provider, and from there Android can bridge between the calling activity and the content provider being called.
An optional feature your content provider offers its clients is notify-on-change support. This means that your content provider will let clients know if the data for a given content Uri
changes.
Читать дальше