[Android Software Development] michael.the.don:
[Ultimate Guide][Part 7-2 released][5/09/2011]Android App Development - From Scratc

PART-VII-2

Introducing Content Providers

Content Providers are a generic interface mechanism that lets you share data between applications. By abstracting away the underlying data source, Content Providers let you decouple your application layer from the data layer, making your applications data-source agnostic.

Content Providers feature full permission control and are accessed using a simple URI model. Shared content can be queried for results as well as supporting write access. As a result, any application with the appropriate permissions can add, remove, and update data from any other applications — including some native Android databases.

Many of the native databases have been made available as Content Providers, accessible by third-party applications. This means that your applications can have access to the phone’s Contact Manager, media player, and other native database once they’ve been granted permission.

Using Content Providers

Access to Content Providers is handled by the ContentResolver class.

Introducing Content Resolvers

Each application Context has a single ContentResolver, accessible using the getContentResolver method, as shown in the following code snippet:

ContentResolver cr = getContentResolver();

Content Resolver includes several methods to transact and query Content Providers. You specify the provider to interact using a URI.

A Content Provider’s URI is defined by its authority as defi ned in its application manifest node. An authority URI is an arbitrary string, so most providers expose a CONTENT_URI property that includes its authority.

Content Providers usually expose two forms of URI, one for requests against all the data and another that specifies only a single row. The form for the latter appends /<rowID> to the standard CONTENT_URI.

Querying for Content

As in databases, query results are returned as Cursors over a result set. Content Provider queries take a very similar form to database queries. Using the query method on the ContentResolver object, pass in:

❑ The URI of the content provider data you want to query

❑ A projection that represents the columns you want to include in the result set

❑ A where clause that defi nes the rows to be returned. You can include ? wild cards that will be replaced by the values stored in the selection argument parameter.

❑ An array of selection argument strings that will replace the ?’s in the where clause

❑ A string that describes the order of the returned rows

The following skeleton code demonstrates the use of a Content Resolver to apply a query to a Content Provider:

// Return all rows

Cursor allRows = getContentResolver().query(MyProvider.CONTENT_URI,

null, null, null, null);

// Return all columns for rows where column 3 equals a set value

// and the rows are ordered by column 5.

String where = KEY_COL3 + “=” + requiredValue;

String order = KEY_COL5;

Cursor someRows = getContentResolver().query(MyProvider.CONTENT_URI,

null, where, null, order);

Adding, Updating, and Deleting Content

To perform transactions on Content Providers, use the delete, update, and insert methods on the ContentResolver object.

Inserts

The Content Resolver offers two methods for inserting new records into your Content Provider — insert and bulkInsert. Both methods accept the URI of the item type you’re adding; where the former takes a single new ContentValues object, the latter takes an array. The simple insert method will return a URI to the newly added record, while bulkInsert returns the number of successfully added items. The following code snippet shows how to use the insert and bulkInsert methods:

// Create a new row of values to insert.

ContentValues newValues = new ContentValues();

// Assign values for each row.

newValues.put(COLUMN_NAME, newValue);

[ ... Repeat for each column ... ]

Uri myRowUri = getContentResolver().insert(MyProvider.CONTENT_URI,

newValues);

// Create a new row of values to insert.

ContentValues[] valueArray = new ContentValues[5];

Deletes

To delete a single record using the Content Resolver, call delete, passing in the URI of the row you want to remove. Alternatively, you can specify a where clause to remove multiple rows. Both techniques are shown in the following snippet:

// Remove a specific row.

getContentResolver().delete(myRowUri, null, null);

// Remove the first five rows.

String where = “_id < 5”;

getContentResolver().delete(MyProvider.CONTENT_URI, where, null);

Updates

Updates to a Content Provider are handled using the update method on a Content Resolver. The update method takes the URI of the target Content Provider, a ContentValues object that maps column names to updated values, and a where clause that specifi es which rows to update. When executed, every matching row in the where clause will be updated using the values in the Content

Values passed in and will return the number of successful updates.

// Create a new row of values to insert.

ContentValues newValues = new ContentValues();

// Create a replacement map, specifying which columns you want to

// update, and what values to assign to each of them.

newValues.put(COLUMN_NAME, newValue);

// Apply to the first 5 rows.

String where = “_id < 5”;

getContentResolver().update(MyProvider.CONTENT_URI, newValues, where,

null);

Accessing Files in Content Providers

Content Providers represent files as fully qualified URIs rather than raw file data. To insert a file into a Content Provider, or access a saved file, use the Content Resolvers openOutputStream or openInputStream methods, respectively. The process for storing a file is shown in the following code snippet:

// Insert a new row into your provider, returning its unique URI.

Uri uri = getContentResolver().insert(MyProvider.CONTENT_URI,

newValues);

try {

// Open an output stream using the new row’s URI.

OutputStream outStream = getContentResolver().openOutputStream(uri);

// Compress your bitmap and save it into your provider.

sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);

}

catch (FileNotFoundException e) { }

Native Android Content Providers

Android exposes many Content Providers that supply access to the native databases. You can use each of these Content Providers natively using the techniques described previously. Alternatively, the android.provider class includes convenience classes that simplify access to many of the most useful providers, including:

❑ Browser Use the browser Content Provider to read or modify bookmarks, browser history, or web searches.

❑ CallLog View or update the call history including both incoming and outgoing calls together with missed calls and call details, like caller ID and call durations.

❑ Contacts Use the Contacts provider to retrieve, modify, or store your contacts’ details.

❑ MediaStore The Media Store provides centralized, managed access to the multimedia on your device, including audio, video, and images. You can store your own multimedia within the Media Store and make it globally available.

❑ Settings You can access the device’s preferences using the Settings provider. Using it, you can view and modify Bluetooth settings, ring tones, and other device preferences.

Using the Media Store Provider

The Android Media Store provides a managed repository for audio, video, and image files. Whenever you add a new multimedia file to the Android filesystem, it should be added to the Media Store to expose it to other applications. The MediaStore class includes a number of convenience methods to simplify inserting files into the Media Store. For example, the following code snippet shows how to insert an image directly into the Media Store:

android.provider.MediaStore.Images.Media.insertImage(

getContentResolver(),

sourceBitmap,

“my_girlfriend_pic”,

“she is in my heaert!”);

Using the Contacts Provider

Access to the Contact Manager is particularly powerful on a communications device. Android does the right thing by exposing all the information available from the contacts database to any application granted the READ_CONTACTS permission. In the following example, an Activity gets a Cursor to every person in the contact database, creating an array of Strings that holds each contact’s name and phone number. To simplify extracting the data from the Cursor, Android supplies public static properties on the People

class that expose the column names.

// Get a cursor over every contact.

Cursor cursor = getContentResolver().query(People.CONTENT_URI,

null, null, null, null);

// Let the activity manage the cursor lifecycle.

startManagingCursor(cursor);

// Use the convenience properties to get the index of the columns

int nameIdx = cursor.getColumnIndexOrThrow(People.NAME);

int phoneIdx = cursor. getColumnIndexOrThrow(People.NUMBER);

String[] result = new String[cursor.getCount()];

if (cursor.moveToFirst())

do {

// Extract the name.

String name = cursor.getString(nameIdx);

// Extract the phone number.

String phone = cursor.getString(phoneIdx);

result[cursor.getPosition()] = name + “ (“ + phone + “)”;

} while(cursor.moveToNext());

To run this code snippet, you need to add the READ_CONTACTS permission to your application.

Creating a New Content Provider

Create a new Content Provider by extending the abstract ContentProvider class. Override the onCreate method to open or initialize the underlying data source you’re exposing with this new provider. The skeleton code for a new Content Provider is shown below:

import android.content.*;

import android.database.Cursor;

import android.net.Uri;

import android.database.SQLException;

public class MyProvider extends ContentProvider {

@Override

public boolean onCreate() {

}

}

You should also expose a public static CONTENT_URI variable that returns the full URI to this provider. Content URIs must be unique between providers, so it’s good practice to base the URI path on your package name. The general form for defi ning a Content Provider’s URI is

content://com.<CompanyName>.provider.<ApplicationName>/<DataPath>

For example:

content://com.sad.provider.myapp/items

Content URIs can represent either of two forms. The previous URI represents a request for all values of that type. Appending a trailing /<rownumber>, as shown below, represents a request for a single record.

content://com.sad.provider.myapp/items/5

It’s good form to support access to your provider using both these forms. The simplest way to do this is using a UriMatcher. Confi gure the UriMatcher to parse URIs to determine their form when the provider is being accessed through a Content Resolver. The following snippet shows the skeleton code for this pattern:

public class MyProvider extends ContentProvider {

private static final String myURI =

“content://com.paad.provider.myapp/items”;

public static final Uri CONTENT_URI = Uri.parse(myURI);

@Override

public boolean onCreate() {

}

// Create the constants used to differentiate between the different

// URI requests.

private static final int ALLROWS = 1;

private static final int SINGLE_ROW = 2;

private static final UriMatcher uriMatcher;

// Populate the UriMatcher object, where a URI ending in ‘items’ will

// correspond to a request for all items, and ‘items/[rowID]’

// represents a single row.

static {

uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

uriMatcher.addURI(“com.paad.provider.myApp”, “items”, ALLROWS);

uriMatcher.addURI(“com.paad.provider.myApp”, “items/#”,

SINGLE_ROW);

}

}

Registering Your Provider

Once you have completed your Content Provider, it must be added to the application manifest. Use the authorities tag to specify its address, as shown in the following XML snippet:

<provider android:name=”MyProvider”

android:authorities=”com.sad.provider.myapp”/>