see 原文链接
Basics of Android : Part IV – Android Content Providers
The last post in the series, which talks about basic Android development concepts. Android application can use a file or SqlLite database to store data. Content provider provides the way by which you can share the data between multiple applications. For example contact data is used by multiple applications and must be stored in Content Provider to have common access. A content provider is a class that implements a standard set of methods to let other applications store and retrieve the type of data that is handled by that content provider. If you want your data to be public and handle by many applications create your own content provider. Application can perform following operations on content provider -
- Querying data
- Modifying records
- Adding records
- Deleting records
Standard Content Provider: Android provide some standard content provider, which are already implemented in Android e.g. contacts, images on device etc. Using these content providers the application can access contact information, images available on the device etc. Querying data: The query string in content provider is different than standard sql query. For any operation like select, add, delete, modify we required content provider URI. The URI consist of three parts, the string “content://”, segment representing kind of data to retrieve and optional ID of specific item of the specified content provider. Here are some examples of query string:
content://media/internal/images URI return the list of all internal images on the device.
content://contacts/people/ URI return the list of all contact names on the device.
content://contacts/people/45 URI return the single result row, the contact with ID=45.
Although this is the general form of the query, query URIs are somewhat arbitrary and confusing. For this android provide list of helper classes in android.provider package that define these query strings so you should not need to know the actual URI value for different data types. So it will be easy to query data. Above URIs can be represented as:
MediaStore.Images.Media.INTERNAL_CONTENT_URI
Contacts.People.CONTENT_URI
To query about specific record we have to use same CONTENT_URI, but must append specific ID. So third URI becomes
Uri person = ContentUris.withAppendedId(People.CONTENT_URI, 23);
Here is how we can query for data:
Cursor cur = managedQuery(person, null, null, null);
This query returns the cursor which contains the fields, we can iterate through cursor to retrieve all data. Let’s see an example now, which will make concept clearer:
package com.wissen.testApp;
public class ContentProviderDemo extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
displayRecords();
}
private void displayRecords() {
// An array specifying which columns to return.
String columns[] = new String[] { People.NAME, People.NUMBER };
Uri mContacts = People.CONTENT_URI;
Cursor cur = managedQuery(mContacts, columns, // Which columns to return
null, // WHERE clause; which rows to return(all rows)
null, // WHERE clause selection arguments (none)
null // Order-by clause (ascending by name)
);
if (cur.moveToFirst()) {
String name = null;
String phoneNo = null;
do {
// Get the field values
name = cur.getString(cur.getColumnIndex(People.NAME));
phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER));
Toast.makeText(this, name + " " + phoneNo, Toast.LENGTH_LONG).show();
} while (cur.moveToNext());
}
}
}
In above example we are retrieving only specific columns contact name, number and displaying the contact records one by one. Modifying Records: To modify the set of records call the method ContentResolver.update() with columns and value to be changed. You can select the columns to be updated in Content query string or in method itself. Here is how it can be done:
private void updateRecord(int recNo, String name) {
//appending recNo, record to be updated
Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo);
ContentValues values = new ContentValues();
values.put(People.NAME, name);
getContentResolver().update(uri, values, null, null);
}
You can call above method to modify any record by specifying record number and name.
updateRecord(10,"XYZ");
This will change the name to “XYZ” of record number 10. Adding Records: To add a new record, call ContentResolver.insert() with the URI of the type of item to add, and a Map of any values you want to set immediately on the new record. This will return the full URI of the new record, including record number, which you can then use to query and get a Cursor over the new record. In above example what we have used is standard Content Provider, Contacts. We can add records in contacts also. Continuing our previous example, let us create insertRecord() method that will do the work of insertion.
private void insertRecords(String name, String phoneNo) {
ContentValues values = new ContentValues();
values.put(People.NAME, name);
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
Log.d("ANDROID", uri.toString());
Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
values.clear();
values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.NUMBER, phoneNo);
getContentResolver().insert(numberUri, values);
}
To insert record in contacts just call insertRecords(name, phoneNo). Deleting records: To delete record/records from content provider use getContextResolver.delete(). Following example deletes all contacts from device as no where condition is specified.
private void deleteRecords() {
Uri uri = People.CONTENT_URI;
getContentResolver().delete(uri, null, null);
}
You can delete specific records by specifying where condition in method as parameter. This will give you an idea, how to delete specific records:
getContentResolver().delete(uri, "NAME=" + "'XYZ XYZ'", null);
This will delete records whose name is ‘XYZ XYZ’. Creating Content Provider: We now know how to use the content Provider; let’s now see how to create a content provider. To create own Content Provider in android following steps needs to be followed: 1. Create a class that will extend ContentProvider. 2. Define a public static final Uri named CONTENT_URI. This is the string that represents the full “content://” URI that your content provider handles. You must define a unique string for this value; the best solution is to use the fully-qualified class name of your content provider (lowercase). So, for example:
public static final Uri CONTENT_URI = Uri.parse( "content://com.google.android.MyContentProvider");
3. Create your system for storing data. Most content providers store their data using Android’s file storage methods or SQLite databases, but you can store your data any way you want, so long as you follow the calling and return value conventions. 4. Define column names that you will return to client. If you are using android database the column will be same as database. But you database must include one column call as _id, which identify each record in database uniquely. 5. If you are exposing byte data, such as a bitmap file, the field that stores this data should actually be a string field with a content:// URI for that specific file. This is the field that clients will call to retrieve this data. The content provider for that content type (it can be the same content provider or another content provider – for example, if you’re storing a photo you would use the media content provider) should implement a field named _data for that record. The _data field lists the exact file path on the device for that file. This field is not intended to be read by the client, but by the ContentResolver. The client will call ContentResolver.openOutputStream() on the user-facing field holding the URI for the item (for example, the column named photo might have value content://media/images/4453). The ContentResolver will request the _data field for that record, and because it has higher permissions than a client, it should be able to access that file directly and return a read wrapper for that file to the client. 6. Declare public static String for client to specify which column to return or to specify field value from cursor. 7. Return a Cursor object in response to query. This means write down all the overridden methods such as insert(), update() and delete() those will perform operations on underlying database. We may notify the listeners about updated information by using ContentResover().notifyChange(). 8. Add <provider> tag to AndroidMenifest.xml and set its authorities to define the authority part of the content type it should handle. 9. If you are handling a new data type, you must define a new MIME type to return for your implementation of android.ContentProvider.geType(url). This type corresponds to the content:// URI submitted to getType(), which will be one of the content types handled by the provider. The MIME type for each content type has two forms: one for a specific record, and one for multiple records. Use the Uri methods to help determine what is being requested. Here is the general format for each:
vnd.android.cursor.item/vnd.yourcompanyname.contenttype for a single row.
For example, a request for train record 122 using content://com.example.transportationprovider/trains/122 might return the MIME type vnd.android.cursor.item/vnd.example.rail
vnd.android.cursor.dir/vnd.yourcompanyname.contenttype for multiple rows.
For example, a request for all train records using content://com.example.transportationprovider/trains might return the MIME type vnd.android.cursor.dir/vnd.example.rail Here is the code that creates a content provider. The example just store user name and display the user names of all the users. SQLLite database is used to store the user data.
package com.wissen.testApp;
public class MyUsers {
public static final String AUTHORITY = "com.wissen.MyContentProvider";
// BaseColumn contains _id.
public static final class User implements BaseColumns {
public static final Uri CONTENT_URI = Uri.parse("content://com.wissen.MyContentProvider");
// Table column
public static final String USER_NAME = "USER_NAME";
}
}
The above class defines the CONTENT_URI of the content provider, it also defines the columns of the content provider. Next we will define the actual content provider class that will use the above defined class.
package com.wissen.testApp.android;
public class MyContentProvider extends ContentProvider {
private SQLiteDatabase sqlDB;
private DatabaseHelper dbHelper;
private static final String DATABASE_NAME = "Users.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "User";
private static final String TAG = "MyContentProvider";
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
//create table to store user names
db.execSQL("Create table " + TABLE_NAME + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
@Override
public int delete(Uri uri, String s, String[] as) {
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentvalues) {
// get database to insert records
sqlDB = dbHelper.getWritableDatabase();
// insert record in user table and get the row number of recently inserted record
long rowId = sqlDB.insert(TABLE_NAME, "", contentvalues);
if (rowId > 0) {
Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();
getContext().getContentResolver().notifyChange(rowUri, null);
return rowUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public boolean onCreate() {
dbHelper = new DatabaseHelper(getContext());
return (dbHelper == null) ? false : true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
SQLiteDatabase db = dbHelper.getReadableDatabase();
qb.setTables(TABLE_NAME);
Cursor c = qb.query(db, projection, selection, null, null, null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
return 0;
}
}
So we created a content Provider class as MyContentProvider and wrote code for insertion and retrieving records from Sqlite database. The content provider entry has to be added in the AndroidManifest.xml file as follows:
<provider android:name="MyContentProvider" android:authorities="com.wissen.MyContentProvider" />
Now Lets use the above defined content provider:
package com.wissen.testApp;
public class MyContentDemo extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
insertRecord("MyUser");
displayRecords();
}
private void insertRecord(String userName) {
ContentValues values = new ContentValues();
values.put(MyUsers.User.USER_NAME, userName);
getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
}
private void displayRecords() {
// An array specifying which columns to return.
String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
Uri myUri = MyUsers.User.CONTENT_URI;
Cursor cur = managedQuery(myUri, columns, // Which columns to return
null, // WHERE clause; which rows to return(all rows)
null, // WHERE clause selection arguments (none)
null // Order-by clause (ascending by name)
);
if (cur.moveToFirst()) {
String id = null;
String userName = null;
do {
// Get the field values
id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
Toast.makeText(this, id + " " + userName, Toast.LENGTH_LONG).show();
} while (cur.moveToNext());
}
}
}
The above class first adds a user entry in the database and then displays all the usernames available in the db. So we saw how to user the content providers and how to create our own content provider. From this post on wards we will be diving deep into specific APIs like Android Media API, Android Communications API, Accelarometer and lots of other stuff.