Using a Content Provider

In the Android security model (see the discussion in Section 2.5, Safe and Secure, on page 40), files written by one application cannot be read from or written to by any other application. Each program has its own Linux user ID and data directory (/data/data/packagename) and its own protected memory space. Android programs can communicate with each other in two ways:

• Inter-Process Communication (IPC): One process declares an arbitrary API using the Android Interface Definition Language (AIDL) and the IBinder interface. Parameters are marshaled safely and efficiently between processes when the API is called. This advanced technique is used for remote procedure calls to a background Service thread.2

2. IPC, services, and binders are beyond the scope of this book. For more information, see http://d.android.com/guide/developing/tools/aidl.html, http://d.android.com/reference/android/app/Service.html, and http://d.android.com/reference/android/os/IBinder.html.

• ContentProvider : Processes register themselves to the system as providers of certain kinds of data. When that information is requested, they are called by Android through a fixed API to query or modify the content in whatever way they see fit. This is the technique we're going to use for the Events sample.

Any piece of information managed by a ContentProvider is addressed through a URI that looks like this:

content://authority /path /id where:

• content:// is the standard required prefix.

• authority is the name of the provider. Using your fully qualified package name is recommended to prevent name collisions.

• path is a virtual directory within the provider that identifies the kind of data being requested.

• id is the primary key of a specific record being requested. To request all records of a particular type, omit this and the trailing slash.

Android comes with several providers already built in, including the following:3

To demonstrate using a ContentProvider, let's convert the Events example to use one. For our Events provider, these will be valid URIs:

content://org.examp1e.events/events/3 -- single event with _id=3 content://org.examp1e.events/events -- all events

First we need to add a two more constants to Constants.java:

Download Eventsv3/src/org/example/events/Constants.java

import android.net.Uri;

public static final String AUTHORITY = "org.example.events";

public static final Uri CONTENT_URI = Uri.parseC'content://" + AUTHORITY + "/" + TABLE_NAME);

3. For an up-to-date list, see http://d.android.com/reference/android/provider/package-summaryhtml. Instead of using the strings here, use the documented constants such as Browser.BOOKMARKS_URI. Note that access to some providers requires additional permissions to be requested in your manifest file.

The layout files (main.xml and item.xml) don't need to be changed, so the next step is to make a few minor changes to the Events class.

Changing the Main Program

The main program (the Events.onCreate() method) actually gets a little simpler because there is no database object to keep track of:

Download Eventsv3/src/org/example/events/Events.java

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); addEvent("Hello, Android!"); Cursor cursor = getEvents(); showEvents(cursor);

We don't need the try/finally block, and we can remove references to EventData.

Adding a Row

Two lines change in addEvent( ). Here's the new version:

Download Eventsv3/src/org/example/events/Events.java

import static org.example.events.Constants.CONTENT_URI; private void addEvent(String string) {

// Insert a new record into the Events data source. // You would do something similar for delete and update. ContentValues values = new ContentValues(); values.put(TIME, System.currentTimeMillis()); values.put(TITLE, string);

getContentResolver().insert(CONTENT_URI, values);

The call to getWritableDatabase( ) is gone, and the call to insertOrThrow( ) is replaced by getContentResolver().insert(). Instead of a database handle, we use a content URI.

Running a Query

The getEvents() method is also simplified when using a ContentProvider:

Download Eventsv3/src/org/example/events/Events.java

private Cursor getEvents() {

// Perform a managed query. The Activity will handle closing

// and re-querying the cursor when needed.

return managedQuery(CONTENT_URI, FROM, null, null, ORDER_BY);

Here we use the Activity.managedQuery() method, passing it the content URI, the list of columns we're interested in, and the order they should be sorted in.

By removing all references to the database, we've decoupled the Events client from the Events data provider. The client is simpler, but now we have to implement a new piece we didn't have before.

0 0

Post a comment