轉自:http://cthhqu.blog.51cto.com/7598297/1281217
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package
cth.android.contentprovide;
import
android.annotation.SuppressLint;
import
android.app.Activity;
import
android.content.ContentResolver;
import
android.database.Cursor;
import
android.os.Bundle;
import
android.provider.ContactsContract;
import
android.util.Log;
import
android.widget.ListAdapter;
import
android.widget.ListView;
import
android.widget.SimpleCursorAdapter;
/**
* @author CTH
*
*該類是演示利用ContentProvider,獲取手機的聯系人信息。
*使用ContentProvide的步驟:
*1、從當前Activity獲取系統的ContentResolver;
*2、使用ContentProvider的insert、delete、update、query方法對ContentProvider的內容進行增刪改查;
*3、如果是使用query是的到一個Cursor的結果集,通過該結果集可以獲得我們查詢的結果。
*
*/
public
class
MainActivity
extends
Activity {
private
ListView contactsList;
@SuppressLint
(
"InlinedApi"
)
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main); //不使用inflate XML文件方法,而是使用動態生成控件。
contactsList =
new
ListView(MainActivity.
this
);
ContentResolver cr = getContentResolver();
//獲取ContentResolver
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,
null
,
null
,
null
,
null
);
//查詢系統的聯系人信息
Log.i(
"cth"
, cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME) +
""
);
@SuppressWarnings
(
"deprecation"
)
ListAdapter la =
new
SimpleCursorAdapter(MainActivity.
this
,
android.R.layout.simple_list_item_1, cursor,
new
String[] {ContactsContract.Contacts.DISPLAY_NAME_PRIMARY },
new
int
[] { android.R.id.text1 });
//建立列表適配器,把cursor關聯進來
contactsList.setAdapter(la);
//把ListVIew與適配器綁定
setContentView(contactsList);
//動態生成ListView
}
}
|
聯系人的URI:
ContactsContract.Contacts.CONTENT_URI 管理聯系人的Uri
ContactsContract.CommonDataKinds.Phone.CONTENT_URI 管理聯系人的電話的Uri
ContactsContract.CommonDataKinds.Email.CONTENT_URI 管理聯系人的Email的Uri
(注:Contacts有兩個表,分別是rawContact和Data,rawContact記錄了用戶的id和name,
其中id欄名稱 為:ContactsContract.Contacts._ID,name名稱欄為ContactContract.Contracts.DISPLAY_NAME,
電話信息表的外鍵id為 ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
電話號碼欄名稱為:ContactsContract.CommonDataKinds.Phone.NUMBER。
data表中Email地址欄名稱為:ContactsContract.CommonDataKinds.Email.DATA
其外鍵欄為:ContactsContract.CommonDataKinds.Email.CONTACT_ID)
多媒體的ContentProvider的Uri如下:
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 存儲在sd卡上的音頻文件
MediaStore.Audio.Media.INTERNAL_CONTENT_URI 存儲在手機內部存儲器上的音頻文件
MediaStore.Audio.Images.EXTERNAL_CONTENT_URI SD卡上的圖片文件內容
MediaStore.Audio.Images.INTERNAL_CONTENT_URI 手機內部存儲器上的圖片
MediaStore.Audio.Video.EXTERNAL_CONTENT_URI SD卡上的視頻
MediaStore.Audio.Video.INTERNAL_CONTENT_URI 手機內部存儲器上的視頻
(注:圖片的顯示名欄:Media.DISPLAY_NAME,圖片的詳細描述欄為:Media.DESCRIPTION 圖片的保存位置:Media.DATA
短信URI: Content://sms
發送箱中的短信URI: Content://sms/outbox
content://authority/path/id
-
content:
-
The scheme portion of the URI. This is always set to
ContentResolver.SCHEME_CONTENT
(valuecontent://
). - authority
- A string that identifies the entire content provider. All the content URIs for the provider start with this string.
- To guarantee a unique authority, providers should consider using an authority that is the same as the provider class' package identifier.
- path
-
Zero or more segments, separated by a forward slash (
/
), that identify some subset of the provider's data. - Most providers use the path part to identify individual tables. Individual segments in the path are often called "directories"
- although they do not refer to file directories. The right-most segment in a path is often called a "twig"
- id
- A unique numeric identifier for a single row in the subset of data identified by the preceding path part.
- Most providers recognize content URIs that contain an id part and give them special handling.
-
A table that contains a column named
_ID
often expects the id part to be a particular value for that column.
由上述我們可以得到該URI分為四部分:
第一部分:“content://”是系統規定的;
第二部分:authority是一個標志整個內容提供者的字符串,也就是該內容提供者的標識符,相當於我們每個人都有的姓名;
第三部分:是內容提供者提供數據的某個子集,因為內容提供者有時會提供多個數據,我們具體是要訪問那個子集,需要把具體路徑標出來;
第四部分:是一個標志數據子集中具體某一行數據的數字,一般我們數據庫都會有ID這一項,通過該字段可直接定位到表中的某一行。如果不知道或用不到該項可不填。
Android里面提供了兩個類對URI進行操作,一個是UriMatcher,該類專用於在ContentProvider中建立匹配器,從安卓的API文檔我們得知該匹配器用於在ContentProvider類的最開始建立匹配器並添加匹配規則,在后面的方法覆蓋過程,需要用到該類的match方法進行匹配,返回匹配到的標志位。
void | addURI(String authority, String path, int code) // 三個參數,第一個是授權信息,第二個是路徑,第三個就是當匹配該規則是返回的標志位
Add a URI to match, and the code to return when this URI is matched.
|
另外一個是ContentUri類,該類僅有的三個方法都是靜態類,它是一個提供對URI和ID進行操作的工具類。常用的主要是parseId:用於取得URI中的Id,其實內部實現方法就是取得Uri PATH部分后面的值;withAppendedId用於把ID拼接到一個Uri的后面。
Public Methods | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
static Uri.Builder | appendId(Uri.Builder builder, long id)
Appends the given ID to the end of the path.
|
||||||||||
static long | parseId(Uri contentUri)
Converts the last path segment to a long.
|
||||||||||
static Uri | withAppendedId(Uri contentUri, long id)
Appends the given ID to the end of the path.
|
2.3.2 ContentProvider的使用方法:
了解了什么是URI就可以自定義一個ContentProvider,對外提供訪問我們數據的接口了。下面以ContentProvider最常封裝的數據類型——數據庫為例子進行說明。
整個自定義ContentProvider的過程分為兩大步,第一步當然是建立數據源,第二部則是建立訪問數據源的內容提供者,這里以SQL數據庫為例:
第一步:首先我們需要先建一個SQLiteOpenHelper的子類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import
android.content.Context;
import
android.database.sqlite.SQLiteDatabase;
import
android.database.sqlite.SQLiteOpenHelper;
public
class
StuDbHelper
extends
SQLiteOpenHelper {
private
static
String DbName =
"student.db"
;
public
StuDbHelper(Context context,
int
version) {
super
(context, DbName,
null
, version);
}
@Override
public
void
onCreate(SQLiteDatabase db) {
String sql =
"create table student (id integer primary key,name varchar(20),age integer)"
;
//在數據庫中創建一張表
db.execSQL(sql);
}
@Override
public
void
onUpgrade(SQLiteDatabase db,
int
oldVersion,
int
newVersion) {
}
}
|
第二步:有了數據源,我們接下來就得建立訪問數據源的ContentProvider,建立ContentProvider的步驟通常分為:建立匹配器並添加匹配規則,重寫各種操作數據的方法。添加匹配規則必需在所有方法和構造函數的執行前執行,因為有時候我們需要在匹配成功后才建立數據庫的SQLiteOpenHelper。所以這里必須使用靜態代碼塊添加匹配規則。而重寫訪問數據的方法主要有增刪改查四種,重寫的步驟基本遵循: 對Uri進行匹配 --> 獲取讀或寫數據庫對象 --> 根據匹配結果利用switch語句判斷該執行哪種操作 --> 返回結果。一下是ContentProvider的源碼示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
package
cth.android.contentprovider;
import
android.content.ContentProvider;
import
android.content.ContentUris;
import
android.content.ContentValues;
import
android.content.UriMatcher;
import
android.database.Cursor;
import
android.database.sqlite.SQLiteDatabase;
import
android.net.Uri;
public
class
StuDbCP
extends
ContentProvider {
private
static
final
UriMatcher URI_MATCHER =
new
UriMatcher(UriMatcher.NO_MATCH);
//創建該內容提供者的匹配器
private
static
final
String AUTHORITY =
"cth.android.contentprovider.StuDbCP"
;
//定義授權信息,用於獲取該內容提供者的標識
private
static
final
String PATH =
"student"
;
//路徑,表示訪問內容提供者的具體路徑,一般用表明表示訪問該數據庫的具體哪張表
private
static
final
int
STU =
1
;
//設定標志位,STU表示單條信息
private
static
final
int
STUS =
2
;
//STUS表示多條信息
static
{
URI_MATCHER.addURI(AUTHORITY, PATH +
"/#"
, STU);
//給匹配器加入匹配規則
URI_MATCHER.addURI(AUTHORITY, PATH, STUS);
}
private
StuDbHelper stuDbHepler =
null
;
private
SQLiteDatabase stuDb =
null
;
@Override
public
boolean
onCreate() {
boolean
flag =
false
;
stuDbHepler =
new
StuDbHelper(getContext(),
1
);
//創建SQLiteOpenHelper對象,版本為1
if
(stuDb !=
null
) flag =
true
;
return
flag;
}
@Override
public
Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
int
flag = URI_MATCHER.match(uri);
//匹配傳入的Uri
Cursor selectResult =
null
;
SQLiteDatabase db = stuDbHepler.getReadableDatabase();
String whereClause =
null
;
switch
(flag) {
case
STU:
whereClause =
"id = "
+ ContentUris.parseId(uri);
//如果匹配第一種方式表示后面跟了id,所以要先提取id
if
(selection !=
null
&& selection.equals(
""
)) {
whereClause +=
" and "
+ selection;
//把id加到where條件子句(下面幾種方法此步驟作用同理)
}
selectResult = db.query(
"student"
, projection, whereClause, selectionArgs,
null
,
null
,
null
);
break
;
case
STUS:
selectResult = db.query(
"student"
, projection,selection,selectionArgs,
null
,
null
,
null
);
}
return
selectResult;
}
/*返回uri的路徑擴展部分的信息,一般單個條目返回字段為vnd.android.cursor.item/對應的PATH 而多個條目的以vnd.android.cursor.dir/對應的PATH。*/
@Override
public
String getType(Uri uri) {
int
flag = URI_MATCHER.match(uri);
switch
(flag) {
case
STU:
return
"vnd.android.cursor.item/student"
;
case
STUS:
return
"vnd.android.cursor.dir/students"
;
}
return
null
;
}
@Override
public
Uri insert(Uri uri, ContentValues values) {
int
flag = URI_MATCHER.match(uri);
Uri resultUri =
null
;
switch
(flag) {
case
STUS :
stuDb = stuDbHepler.getWritableDatabase();
//獲取寫數據庫
long
id = stuDb.insert(PATH,
null
, values);
//插入數據
resultUri = ContentUris.withAppendedId(uri, id);
//建立插入的數據的URI
break
;
}
return
resultUri;
}
@Override
public
int
delete(Uri uri, String selection, String[] selectionArgs) {
int
flag = URI_MATCHER.match(uri);
String whereClause =
null
;
int
rowCount = -
1
;
SQLiteDatabase db = stuDbHepler.getWritableDatabase();
switch
(flag) {
case
STU:
whereClause =
"id = "
+ ContentUris.parseId(uri);
if
(selection !=
null
&& selection.equals(
""
)) {
whereClause +=
" and "
+ selection ;
}
rowCount = db.delete(
"student"
, whereClause, selectionArgs);
break
;
case
STUS:
rowCount = db.delete(
"student"
, selection, selectionArgs);
default
:
break
;
}
return
rowCount;
}
@Override
public
int
update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int
flag = URI_MATCHER.match(uri);
int
count = -
1
;
SQLiteDatabase db = stuDbHepler.getWritableDatabase();
String whereClause =
null
;
switch
(flag) {
case
STU:
whereClause =
"id = "
+ ContentUris.parseId(uri);
if
(selection !=
null
&& selection.equals(
""
)) {
whereClause +=
" and "
+ selection ;
}
count = db.update(
"student"
, values, whereClause, selectionArgs);
break
;
case
STUS:
count = db.update(
"student"
, values, selection, selectionArgs);
break
;
}
return
count;
}
}
|
最后,ContentProvider也是安卓的四大組件之一,所以我們要在Manifest文件的application標簽中對其進行注冊。這樣,一個內容提供者就建立好了。我們可以另外建立一個測試工程,對其進行跨進程訪問,不過需要注意的是我們對其進行訪問前必須確定該ContentProvider是存在的,所以必須先運行ContentProvider的工程,然后就可以使用測試工程對其進行訪問了。這里需要注意的是,當我們把ContentProvider進程關閉后,我們還是可以對其數據進行訪問的。一下是測試工程的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
import
android.content.ContentResolver;
import
android.content.ContentValues;
import
android.database.Cursor;
import
android.net.Uri;
import
android.test.AndroidTestCase;
import
android.util.Log;
/*
* 新建一個工程,建立一個測試類來測試是否能夠跨進程訪問數據。分別實現增刪改查四種方法。
* */
public
class
TestCP
extends
AndroidTestCase {
public
void
insertData() {
ContentResolver cr = getContext().getContentResolver();
ContentValues values =
new
ContentValues();
values.put(
"id"
,
3
);
values.put(
"name"
,
"Jiky"
);
values.put(
"age"
,
18
);
Uri resultUri = cr.insert(uri, values);
if
(resultUri !=
null
) {
Log.i(
"cth"
,resultUri.toString());
}
else
{
Log.e(
"cth"
,
"插入失敗"
);
}
}
public
void
deleteData() {
ContentResolver cr = getContext().getContentResolver();
String where =
"id = ?"
;
int
deleteRowNum = cr.delete(uri, where,
new
String[] {
"123"
});
Log.i(
"cth"
,
"deleteRowNum is "
+ deleteRowNum );
}
public
void
updateData() {
ContentResolver cr = getContext().getContentResolver();
Uri uri = Uri.parse(
"content://cth.android.contentprovider.StuDbCP/student/"
); //直接把要修改的id加在PATH后,也可按一下方式。
ContentValues values =
new
ContentValues();
values.put(
"name"
,
"Mike"
);
values.put(
"age"
,
11
);
int
rowId = cr.update(uri, values,
"id = ?"
,
new
String[]{
"1"
});
if
(rowId ==
0
) {
Log.e(
"cth"
,
"找不到匹配項。"
);
}
else
{
Log.i(
"cth"
,
"rowId = "
+ rowId);
}
}
public
void
selectData() {
ContentResolver cr = getContext().getContentResolver();
Cursor cursor = cr.query(uri,
new
String[]{
"name"
,
"id"
},
null
,
null
,
null
);
while
(cursor.moveToNext()) {
Log.i(
"cth"
,cursor.getString(cursor.getColumnIndex(
"id"
)) +
" count = "
+ cursor.getCount());
}
}
}
|