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).
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.
Notify-on-Change Support
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.
For example, suppose you have created a content provider that retrieves RSS and Atom feeds from the Internet based on the user’s feed subscriptions (via OPML, perhaps). The content provider offers read-only access to the contents of the feeds, with an eye toward several applications on the phone using those feeds versus everyone implementing their own feed-poll-fetch-and-cache system. You have also implemented a service that will get updates to those feeds asynchronously, updating the underlying data store. Your content provider could alert applications using the feeds that such-and-so feed was updated, so applications using that specific feed could refresh and get the latest data.
On the content-provider side, to do this call notifyChange()
on your ContentResolver
instance (available in your content provider via getContext ().getContentResolver()
). This takes two parameters: the Uri of the piece of content that changed, and the ContentObserver
that initiated the change. In many cases, the latter will be null
; a non-null
value simply means the observer that initiated the change will not be notified of its own changes.
On the content-consumer side, an activity can call registerContentObserver()
on its ContentResolver
(via getContentResolver()
). This ties a ContentObserver
instance to a supplied Uri — the observer will be notified whenever notifyChange()
is called for that specific Uri
. When the consumer is done with the Uri
, unregisterContentObserver()
releases the connection.
CHAPTER 29
Requesting and Requiring Permissions
In the late 1990s a wave of viruses spread through the Internet, delivered via email, using contact information culled from Microsoft Outlook. A virus would simply email copies of itself to each of the Outlook contacts that had an email address. This was possible because, at the time, Outlook did not take any steps to protect data from programs using the Outlook API, since that API was designed for ordinary developers, not virus authors.