轉自:http://cthhqu.blog.51cto.com/7598297/1281217html
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的步驟:
*一、從當前Activity獲取系統的ContentResolver;
*二、使用ContentProvider的insert、delete、update、query方法對ContentProvider的內容進行增刪改查;
*三、若是是使用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,java
其中id欄名稱 爲:ContactsContract.Contacts._ID,name名稱欄爲ContactContract.Contracts.DISPLAY_NAME,android
電話信息表的外鍵id爲 ContactsContract.CommonDataKinds.Phone.CONTACT_ID,sql
電話號碼欄名稱爲:ContactsContract.CommonDataKinds.Phone.NUMBER。數據庫
data表中Email地址欄名稱爲:ContactsContract.CommonDataKinds.Email.DATA
其外鍵欄爲:ContactsContract.CommonDataKinds.Email.CONTACT_ID)api
多媒體的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 手機內部存儲器上的視頻app
(注:圖片的顯示名欄:Media.DISPLAY_NAME,圖片的詳細描述欄爲:Media.DESCRIPTION 圖片的保存位置:Media.DATAide
短信URI: Content://sms函數
發送箱中的短信URI: Content://sms/outbox
content://authority/path/id
content:
ContentResolver.SCHEME_CONTENT
(value
content://
).
/
), that identify some subset of the provider's data.
_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());
}
}
}
|