ContentProvider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。
也就是说,一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据暴露出去。
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,外界可以通过这一套标准及统一的接口和程序里的数据打交道,其常见方法如下:
• query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri 进行查询,返回一个Cursor。
• insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
• update(Uri uri, ContentValues values, String where, String[] selectionArgs) 更新Uri 指定位置的数据。
• delete(Uri url, String where, String[] selectionArgs) 删除指定Uri 并且符合一定条件的数据
外界的程序通过ContentResolver 接口可以访问ContentProvider 提供的数据,在Activity 当中通过getContentResolver()可以得到当前应用的ContentResolver 实例。
其常用方法如下:
• query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri 进行查询,返回一个Cursor。
• insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
• update(Uri uri, ContentValues values, String where, String[] selectionArgs) 更新Uri 指定位置的数据。
• delete(Uri url, String where, String[] selectionArgs) 删除指定Uri 并且符合一定条件的数据
在ContentProvider 和ContentResolver 当中用到了Uri 的形式通常有两种,一种是指定全部数据,另一种是指定某个ID 的数据。我们看下面的例子。
• content://contacts/people/ 这个 Uri 指定的就是全部的联系人数据。
• content://contacts/people/1 这个 Uri 指定的是ID 为1 的联系人的数据。
在上边两个类中用到的Uri 一般由3 部分组成
• 第一部分是:"content://" 。
• 第二部分是要获得数据的一个字符串片段。
• 最后就是ID(如果没有指定ID,那么表示返回全部)。
实现自定义的ContentProvider步骤及示例代码如下:
(1)继承ContentProvider,本例中为DiaryContentProvider类。
(2)定义一个 public static final 的Uri 类型的变量,并且命名为CONTENT_URI。DiaryContentProvider 所能处理的Uri都是基于CONTENT_URI 来构建的,需要注意的是,这个CONTENT_URI 中的内容以content://开头,并且全部小写,且全局惟一。下边是在这个例子中的一个普通URI,通过分析这个URI,可以了解content URI 的构成,
代码如下所示:content://com.ex09_2_contentprovider.diarycontentprovider/diaries/1
●第一部分是content://,这部分是支持存在的,也是不用做什么修改的。
●第二部分是授权(AUTHORITY)部分,在这个例子里边就是com.ex09_2_contentprovider.diarycontentprovider,授权部分是惟一的,
一般为在程序是我们实现的那个ContentProvider的全称,并且全都小写。这部份是和在AndroidManifest.xml文件当中的<providerAndroid:name="DiaryContentProvider" Android:authorities="com.ex09_2_contentprovider.diarycontentprovider" />部分对应的。
●第三部分是请求数据的类型。例如,在这个例子当中定义的类型是diaries。当然这一部分可以是0 个片段或者多个片段构成,
如果content provider 只是暴露出了一种类型的数据,那么这部分可以为空,但是如果暴露出了多种,尤其是包含子类的的时候,就不能为空。
例如,日记本程序里边可以暴露出来两种数据,一种是用户自己的"diaries/my",另一种是其他人的"diaries/others。
●第四部分就是“1”,当然这部分是允许为空的。如果为空,表示请求全部数据;如果不为空,表示请求特定ID的数据。
(3).构建用户的数据存储系统。在这个例子当中,是将数据存储到数据库系统当中。通常是将数据存储在数据库系统中,但是也可以将数据存储在其他的地方,如文件系统等。
1
private
static
class
DatabaseHelper
extends
SQLiteOpenHelper {
2
DatabaseHelper(Context context) {
3
super
(context, DATABASE_NAME,
null
, DATABASE_VERSION);
4
}
5
@Override
6
public
void
onCreate(SQLiteDatabase db) {
7
db.execSQL(
"
CREATE TABLE
"
+
DIARY_TABLE_NAME
+
"
(
"
8
+
DiaryColumns._ID
+
"
INTEGER PRIMARY KEY,
"
9
+
DiaryColumns.TITLE
+
"
TEXT,
"
+
DiaryColumns.BODY
10
+
"
TEXT,
"
+
DiaryColumns.CREATED
+
"
TEXT
"
+
"
);
"
);
11
}
12
@Override
13
public
void
onUpgrade(SQLiteDatabase db,
int
oldVersion,
int
newVersion) {
14
db.execSQL(
"
DROP TABLE IF EXISTS notes
"
);
15
onCreate(db);
16
}
17
}
(4).实现ContentProvider 这个抽象类的抽象方法,具体如下所示:
• public boolean onCreate(),当ContentProvider 生成的时候调用此方法,一般在此方法中打开或初始化底层数据库。
• public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) ,此方法返回一个Cursor 对象作为查询结果集。
• public Uri insert(Uri uri, ContentValues initialValues),此方法负责往数据集当中插入一列,并返回这一列的Uri。
• public int delete(Uri uri, String where, String[] whereArgs),此方法负责删除指定Uri 的数据。
• public int update(Uri uri, ContentValues values, String where,String[] whereArgs) ,此方法负责更新指定Uri 的数据。
• public String getType(Uri uri) ,返回所给Uri 的MIME 类型。
1
在 DiaryContentProvider 中,我们定义了一些变量和常量,其中这些常量主要是描述数据库的的信息。
2
private
static
final
String DATABASE_NAME
=
"
database
"
;
3
private
static
final
int
DATABASE_VERSION
=
1
;
4
private
static
final
String DIARY_TABLE_NAME
=
"
diary
"
;
5
private
static
HashMap
<
String, String
>
sDiariesProjectionMap;
6
private
static
final
int
DIARIES
=
1
;
7
private
static
final
int
DIARY_ID
=
2
;
8
private
static
final
UriMatcher sUriMatcher;
其中的UriMatcher 是匹配Uri 的一个辅助类。通过 UriMatcher 类我们可以很方便的判断一个URi 的类型,特别是判断这个Uri 是对单个数据的请求,还是对全部数据的请求。
在我们的DiaryContentProvider 中的static 模块中,有下边的代码:
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(Diary.AUTHORITY, "diaries", DIARIES);
sUriMatcher.addURI(Diary.AUTHORITY, "diaries/#", DIARY_ID);
• sUriMatcher.addURI(Diary.AUTHORITY, "diaries", sUriMatcher.match(uri))表示,
如果我们的Uri 是content://com.ex09_2_contentprovider.diarycontentprovider/diaries/,那么sUriMatcher.match(uri) 的返回值就是DIARIES。
• sUriMatcher.addURI(Diary.AUTHORITY, "diaries/#", DIARY_ID)表示,
如果我们的Uri 是content://com.ex09_2_contentprovider.diarycontentprovider/diaries/id,那么sUriMatcher.match(uri)的返回值就是DIARY_ID。
第四部说说的insert()方法代码如下:
1
public
Uri insert(Uri uri, ContentValues initialValues) {
2
if
(sUriMatcher.match(uri)
!=
DIARIES) {
3
throw
new
IllegalArgumentException(
"
Unknown URI
"
+
uri);
4
}
5
ContentValues values;
6
if
(initialValues
!=
null
) {
7
values
=
new
ContentValues(initialValues);
8
}
else
{
9
values
=
new
ContentValues();
10
}
11
if
(values.containsKey(Diary.DiaryColumns.CREATED)
==
false
) {
12
values.put(Diary.DiaryColumns.CREATED, getFormateCreatedDate());
13
}
14
if
(values.containsKey(Diary.DiaryColumns.TITLE)
==
false
) {
15
Resources r
=
Resources.getSystem();
16
values.put(Diary.DiaryColumns.TITLE, r
17
.getString(Android.R.string.untitled));
18
}
19
if
(values.containsKey(Diary.DiaryColumns.BODY)
==
false
) {
20
values.put(Diary.DiaryColumns.BODY,
""
);
21
}
22
SQLiteDatabase db
=
mOpenHelper.getWritableDatabase();
23
long
rowId
=
db.insert(DIARY_TABLE_NAME, DiaryColumns.BODY, values);
//
insert()返回的是一个Uri,而不是一个记录的id
24
if
(rowId
>
0
) {
25
Uri diaryUri
=
ContentUris.withAppendedId(
26
Diary.DiaryColumns.CONTENT_URI, rowId);
27
return
diaryUri;
28
}
29
throw
new
SQLException(
"
Failed to insert row into
"
+
uri);
30
}
ContentUris是ContentUri的一个辅助类,其withAppendedId()方法负责把id 和contentUri 连接成一个新的Uri。比如在我们这个例子当中是这么使用的:ContentUris.withAppendedId(Diary.DiaryColumns.CONTENT_URI, rowId).如果rowId 为100 的话,那么现在的这个Uri 的内容就是:
content://com.ex09_2_contentprovider.diarycontentprovider/diaries/100.
ContentUris还有一个比较实用的方法parseId(Uri contentUri),这个方法负责把content URI 后边的id 解析出来.比如现在这个contentURI 是content://com.ex09_2_contentprovider.diarycontentprovider/diaries/100,那么这个函数的返回值就是100.
删除方法delete():
1
public
int
delete(Uri uri, String where, String[] whereArgs) {
2
SQLiteDatabase db
=
mOpenHelper.getWritableDatabase();
3
/*
4
*getPathSegments()方法得到一个String 的List,在我们例子
5
*当中uri.getPathSegments().get(1)为rowId,如果是*uri.getPathSegments().get(0),那值就是"diaries"
6
*/
7
String rowId
=
uri.getPathSegments().get(
1
);
8
return
db
9
.delete(DIARY_TABLE_NAME, DiaryColumns._ID
+
"
=
"
+
rowId,
null
);
10
}
更新方法update():
1
public
int
update(Uri uri, ContentValues values, String where,
2
String[] whereArgs) {
3
SQLiteDatabase db
=
mOpenHelper.getWritableDatabase();
4
String rowId
=
uri.getPathSegments().get(
1
);
5
return
db.update(DIARY_TABLE_NAME, values, DiaryColumns._ID
+
"
=
"
6
+
rowId,
null
);
7
}
查询方法query():
1
public
Cursor query(Uri uri, String[] projection, String selection,
2
String[] selectionArgs, String sortOrder) {
3
SQLiteQueryBuilder qb
=
new
SQLiteQueryBuilder();
//
是一个构造SQL 查询语句的辅助类
4
switch
(sUriMatcher.match(uri)) {
5
case
DIARIES:
//
根据返回值可以判断这次查询请求时,它是请求全部数据还是某个id 的数据。
6
qb.setTables(DIARY_TABLE_NAME);
7
break
;
8
case
DIARY_ID:
9
qb.setTables(DIARY_TABLE_NAME);
10
qb.appendWhere(DiaryColumns._ID
+
"
=
"
11
+
uri.getPathSegments().get(
1
));
12
break
;
13
default
:
14
throw
new
IllegalArgumentException(
"
Unknown URI
"
+
uri);
15
}
16
String orderBy;
17
if
(TextUtils.isEmpty(sortOrder)) {
18
orderBy
=
Diary.DiaryColumns.DEFAULT_SORT_ORDER;
19
}
else
{
20
orderBy
=
sortOrder;
21
}
22
SQLiteDatabase db
=
mOpenHelper.getReadableDatabase();
23
/*
第一个参数为要查询的数据库实例。
24
第二个参数是一个字符串数组,里边的每一项代表了需要返回的列名。
25
第三个参数相当于sql 语句中的where 部分。
26
第四个参数是一个字符串数组,里边的每一项依次替代在第三个参数中出现的问号(?)
27
第五个参数相当于sql 语句当中的groupby 部分。
28
第六个参数相当于sql 语句当中的having 部分。
29
第七个参数描述是怎么进行排序。
30
第八个参数相当于sql 当中的limit 部分。控制返回的数据的个数。
31
*/
32
Cursor c
=
qb.query(db, projection, selection, selectionArgs,
null
,
33
null
, orderBy);
34
return
c;
35
}
重写后的getType():
1
public
String getType(Uri uri) {
2
/*
3
此方法返回一个所给Uri 的指定数据的MIME 类型。它的返回值如果以vnd.Android.cursor.item 开头,那么就代
4
表这个Uri 指定的是单条数据。如果是以vnd.Android.cursor.dir 开头的话,那么说明这个Uri 指定的是全部数
5
据。
6
*/
7
switch
(sUriMatcher.match(uri)) {
8
case
DIARIES:
9
//
"vnd.Android.cursor.dir/vnd.google.diary"
10
return
DiaryColumns.CONTENT_TYPE;
11
case
DIARY_ID:
12
//
vnd.Android.cursor.item/vnd.google.diary
13
return
DiaryColumns.CONTENT_ITEM_TYPE;
14
default
:
15
throw
new
IllegalArgumentException(
"
Unknown URI
"
+
uri);
16
}
17
}