我们大家都知道让自己的数据和其它应用程序共享有两种方式:创建自己的Content Provider (即继承自Content Provider的子类) 或者是将自己的数据添加到已有的Content Provider中去,后者需要保证现有的Content Provider和自己的数据类型相同并且具有该 Content Provider的写入的权限。
如果需要创建一个Content Provider,则需要进行的工作主要分为以下3个步骤。
(1) 建立数据的存储系统
数据的存储系统可以由开发人员任意决定,一般来讲,大多数的Content Provider都通过Android的文件存储系统或SQLite 数据库建立自己的数据存储系统。
(2)扩展 ContentProvider类
开发一个继承自ContentProvider类的 子类代码来扩展 ContentProvider类,在这个步骤主要的工作是将要共享的数据包装并以ContentResolver 和 Cursor对象能够访问到的形式对外展示。具体来说需要实现ContentProvider 类中的6个抽象方法。
Cursor query(Uri uri, String[]
projection, String selection, String[] selectionArgs, String sortOrder):将查询的数据以Cursor 对象的形式返回。
Uri insert(Uri uri, Content
Values values):向 Content Provider中插入新数据记录,ContentValues 为数据记录的列名和列值映射。
int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):更新Content Provider中已存在的数据记录。
int delete(Uri uri, String selection, String[] selectionArgs):从Content Provider中删除数据记录。
String getType(Uri uri):返回Content Provider中的数据( MIME )类型。
boolean onCreate():当 Content Provider 启动时被调用。
以上方法将会在ContentResolver 对象中调用,所以很好地实现这些抽象方法会为ContentResolver提供一个完善的外部
接口。除了实现抽象方法外,还可以做一些提高可用性的工作。
定义一个 URI 类型的静态常量,命名为CONTENT_URI。 必须为该常量对象定义一个唯一的URI字符串,一般的做法是将 ContentProvider子类的全称类名作为URI字符串,如:
"content://wyf.wpf.MyProvider"。
定义每个字段的列名,如果采用的数据库存储系统为SQLite 数据库,
数据表列名可以采用数据库中表的列名。不管数据表中有没有其他的唯一标识一个记录的字段,都应该定义一个"_id"字段 来唯一标识一个记录。模式使用 "INTEGER PRIMARY
KEY AUTOINCREMENT" 自动更新 一般将这些列名字符串定义为静态常量, 如"_id"字段名定义为一个名为"_ID" 值为 "_id" 的静态字符串对象。
(3)在应用程序的AdnroidManifest.xml 文件中声明Content Provider组件。
创建好一个Content Provider必须要在应用程序的AndroidManifest.xml 中进行声明,否则该Content Provider对于 Android系统将是不可见的。如果有一个名为MyProvider的类扩展了 ContentProvider类,声明该组件的代码如下:
<provider name="wyf.wpf.MyProvider"
authorities="wyf.wpf.myprovider"
...../> <!-- 为<provider>标记添加name、authorities属性-->
其中name属性为ContentProvider 子类的全称类名,authorities 属性唯一标识了一个ContentProvider。还可以通过 setReadPermission() 和 setWritePermission() 来设置其操作权限。当然也可以再上面的 xml中加入 android:readPermission 或者 android: writePermission属性来控制其权限。
注意:因为ContentProvider可能被不同的进程和线程调用,所以这些方法必须是线程安全的。
下边是一个
例子修改了 SDK 中的 Notes例子。首先创建 ContentProvider 的 CONTENT_URI 和 一些字段数据,字段类可以继承自BaseColumns类,它包括了一些基本的字段,比如:_id等 代码如下:
NotePad类
package xiaohang.zhimeng;
import android.net.Uri;
import android.provider.BaseColumns;
public class NotePad {
//ContentProvider的uri
public static final String AUTHORITY = "com.google.provider.NotePad";
private NotePad(){}
//定义基本字段 实现BaseColumns 这个接口里边已经定义了"_id"字段所以这里不用定义了
public static final class Notes implements BaseColumns{
private Notes(){}
//Uri.parse 方法根据指定字符串创建一个 Uri 对象
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");
//新的MIME类型-多个
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.note";
//新的MIME类型-单个
public static final String CONTENT_ITME_TYPE = "vnd.android.cursor.item/vnd.google.note";
public static final String DEFAULT_SORT_ORDER = "modified DESC";
//字段
public static final String TITLE = "title";
public static final String NOTE = "note";
public static final String CREATEDDATE = "created";
public static final String MODIFIEDDATE = "modified";
}
}
然后我们需要来创建自己的 ContentProvider 类的 NotePadProvider,它包括了查询、添加、删除、更新等操作以及打开和创建数据库,代码如下:
NotePadProvider 类
package xiaohang.zhimeng;
import java.util.HashMap;
import xiaohang.zhimeng.NotePad.Notes;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
public class NotePadProvider extends ContentProvider{
private static final String TAG = "NotePadProvider";
//数据库名
private static final String DATABASE_NAME = "note_pad.db";
private static final int DATABASE_VERSION = 2;
//表名
private static final String NOTES_TABLE_NAME = "notes";
private static HashMap<String, String> sNotesProjectionMap;
private static final int NOTES = 1;
private static final int NOTE_ID = 2;
private static final UriMatcher sUriMatcher;
private DatabaseHelper mOpenHelper;
//创建表SQL语句
private static final String CREATE_TABLE="CREATE TABLE"
+ NOTES_TABLE_NAME
+ "(" + Notes._ID
+ "INTEGER PRIMARY KEY,"
+ Notes.TITLE
+ " TEXT,"
+ Notes.NOTE
+ " TEXT,"
+ Notes.CREATEDDATE
+ " INTEGER,"
+ Notes.MODIFIEDDATE
+ " INTEGER" + ");";
static{
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);
sNotesProjectionMap = new HashMap<String, String>();
sNotesProjectionMap.put(Notes._ID, Notes._ID);
sNotesProjectionMap.put(Notes.TITLE, Notes.TITLE);
sNotesProjectionMap.put(Notes.NOTE, Notes.NOTE);
sNotesProjectionMap.put(Notes.CREATEDDATE, Notes.CREATEDDATE);
sNotesProjectionMap.put(Notes.MODIFIEDDATE, Notes.MODIFIEDDATE);
}
private static class DatabaseHelper extends SQLiteOpenHelper{
//构造函数-创建数据库
DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//创建表
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
//更新数据库
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}
//删除数据
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case NOTES:
count = db.delete(NOTES_TABLE_NAME, selection, selectionArgs);
break;
case NOTE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.delete(NOTES_TABLE_NAME, Notes._ID + "=" + noteId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unnown URI" + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
//如果有自定类型,必须实现该方法
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case NOTES:
return Notes.CONTENT_TYPE;
case NOTE_ID:
return Notes.CONTENT_ITME_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
//插入数据库
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
if (sUriMatcher.match(uri) != NOTES) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
}else {
values = new ContentValues();
}
//返回以毫秒为单位的系统当前时间
Long now = Long.valueOf(java.lang.System.currentTimeMillis());
/**
* contaisKey()我的理解就是判断传进来的那个ContentValues有没有相应的列值
* 因为我们的一个ContentValues对象 对应一条数据库的记录
* */
if (values.containsKey(NotePad.Notes.CREATEDDATE) == false) {
values.put(NotePad.Notes.CREATEDDATE, now);
}
if (values.containsKey(NotePad.Notes.MODIFIEDDATE) == false) {
values.put(NotePad.Notes.MODIFIEDDATE, now);
}
if (values.containsKey(NotePad.Notes.TITLE) == false) {
//返回一个全局共享的资源对象
Resources r = Resources.getSystem();
values.put(NotePad.Notes.TITLE, r.getString(android.R.string.unknownName));
}
if (values.containsKey(NotePad.Notes.NOTE) == false) {
values.put(NotePad.Notes.NOTE, "");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
throw new SQLException("Failed to insert row into" + uri);
}
//当Content Provider启动时被调用
@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext());
return true;
}
//查询操作 将查询的数据以 Cursor 对象的形式返回
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (sUriMatcher.match(uri)) {
case NOTES:
qb.setTables(NOTES_TABLE_NAME);
qb.setProjectionMap(sNotesProjectionMap);
break;
case NOTE_ID:
qb.setTables(NOTES_TABLE_NAME);
qb.setProjectionMap(sNotesProjectionMap);
qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
String orderBy;
//返回true,如果字符串为空或0长度
if (TextUtils.isEmpty(sortOrder)) {
orderBy = NotePad.Notes.DEFAULT_SORT_ORDER;
}else {
orderBy = sortOrder;
}
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
//用来为Cursor对象注册一个观察数据变化的URI
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
//更新数据
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case NOTES:
count = db.update(NOTES_TABLE_NAME, values, selection, selectionArgs);
break;
case NOTE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.update(NOTES_TABLE_NAME, values, Notes._ID + "=" + noteId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknow URI " + uri);
}
return count;
}
}
下面我们要来创建一个Activity类,首先向其中插入两个数据,然后通过Toast来显示数据库中的数据。 运行效果如下:
代码 Activity01 类
package xiaohang.zhimeng;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.Toast;
public class Activity01 extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/*插入数据*/
ContentValues values = new ContentValues();
values.put(NotePad.Notes.TITLE, "title1");
values.put(NotePad.Notes.NOTE, "NOTENOTE1");
getContentResolver().insert(NotePad.Notes.CONTENT_URI, values);
values.clear();
values.put(NotePad.Notes.TITLE, "title2");
values.put(NotePad.Notes.NOTE, "NOTENOTE2");
getContentResolver().insert(NotePad.Notes.CONTENT_URI, values);
//显示
displayNote();
}
private void displayNote(){
String columns[] = new String[] { NotePad.Notes._ID,
NotePad.Notes.TITLE,
NotePad.Notes.NOTE,
NotePad.Notes.CREATEDDATE,
NotePad.Notes.MODIFIEDDATE};
Uri myUri = NotePad.Notes.CONTENT_URI;
Cursor cur = managedQuery(myUri, columns, null, null, null);
if (cur.moveToFirst()) {
String id = null;
String titile = null;
do {
id = cur.getString(cur.getColumnIndex(NotePad.Notes._ID));
titile = cur.getString(cur.getColumnIndex(NotePad.Notes.TITLE));
Toast toast = Toast.makeText(this, "TITILE:"+id + "\t" + "NOTE:" + titile, Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP|Gravity.CENTER, 0, 40);
toast.show();
} while (cur.moveToNext());
}
}
}
最后不要忘记在AndroidManifest.xml文件中声明我们使用的ContentProvider,下面是我的
AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xiaohang.zhimeng" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<provider android:name="NotePadProvider"
android:authorities="com.xh.google.provider.NotePad" />
<activity android:name=".Activity01"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<data
android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
<intent-filter>
<data
android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="5" />
</manifest>
源码在附件里