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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
package
cn.testcontentprovider;
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;
/**
* Demo描述:
* 自定義ContentProvider的實現
* ContentProvider主要用於在不同的應用程序之間共享數據,這也是官方推薦的方式.
*
* 注意事項:
* 1 在AndroidManifest.xml中注冊ContentProvider時的屬性
* android:exported=true表示允許其他應用訪問.
* 2 注意*和#這兩個符號在Uri中的作用
* 其中*表示匹配任意長度的字符
* 其中#表示匹配任意長度的數據
* 所以:
* 一個能匹配所有表的Uri可以寫成:
* 一個能匹配person表中任意一行的Uri可以寫成:
*
*/
public
class
ContentProviderTest
extends
ContentProvider {
private
SQLiteDatabaseOpenHelper mSQLiteDatabaseOpenHelper;
private
final
static
String AUTHORITY=cn.bs.testcontentprovider;
private
static
UriMatcher mUriMatcher;
private
static
final
int
PERSON_DIR =
0
;
private
static
final
int
PERSON =
1
;
/**
* 利用靜態代碼塊初始化UriMatcher
* 在UriMatcher中包含了多個Uri,每個Uri代表一種操作
* 當調用UriMatcher.match(Uri uri)方法時就會返回該uri對應的code;
* 比如此處的PERSONS和PERSON
*/
static
{
mUriMatcher =
new
UriMatcher(UriMatcher.NO_MATCH);
// 該URI表示返回所有的person,其中PERSONS為該特定Uri的標識碼
mUriMatcher.addURI(AUTHORITY, person, PERSON_DIR);
// 該URI表示返回某一個person,其中PERSON為該特定Uri的標識碼
mUriMatcher.addURI(AUTHORITY, person/#, PERSON);
}
/**
* 在自定義ContentProvider中必須覆寫getType(Uri uri)方法.
* 該方法用於獲取Uri對象所對應的MIME類型.
*
* 一個Uri對應的MIME字符串遵守以下三點:
* 1 必須以vnd開頭
* 2 如果該Uri對應的數據可能包含多條記錄,那么返回字符串應該以vnd.android.cursor.dir/開頭
* 3 如果該Uri對應的數據只包含一條記錄,那么返回字符串應該以vnd.android.cursor.item/開頭
*/
@Override
public
String getType(Uri uri) {
switch
(mUriMatcher.match(uri)) {
case
PERSON_DIR:
return
vnd.android.cursor.dir/+AUTHORITY+.persons;
case
PERSON:
return
vnd.android.cursor.item/+AUTHORITY+.person;
default
:
throw
new
IllegalArgumentException(unknown uri+uri.toString());
}
}
@Override
public
boolean
onCreate() {
mSQLiteDatabaseOpenHelper=
new
SQLiteDatabaseOpenHelper(getContext());
return
true
;
}
/**
* 插入操作:
* 插入操作只有一種可能:向一張表中插入
* 返回結果為新增記錄對應的Uri
* 方法db.insert()返回結果為新增記錄對應的主鍵值
*/
@Override
public
Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
switch
(mUriMatcher.match(uri)) {
case
PERSON_DIR:
long
newId = db.insert(person, name,phone,salary, values);
//向外界通知該ContentProvider里的數據發生了變化 ,以便ContentObserver作出相應
getContext().getContentResolver().notifyChange(uri,
null
);
return
ContentUris.withAppendedId(uri, newId);
default
:
throw
new
IllegalArgumentException(unknown uri + uri.toString());
}
}
/**
* 更新操作:
* 更新操作有兩種可能:更新一張表或者更新某條數據
* 在更新某條數據時原理類似於查詢某條數據,見下.
*/
@Override
public
int
update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {
SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
int
updatedNum =
0
;
switch
(mUriMatcher.match(uri)) {
// 更新表
case
PERSON_DIR:
updatedNum = db.update(person, values, selection, selectionArgs);
break
;
// 按照id更新某條數據
case
PERSON:
long
id = ContentUris.parseId(uri);
String where = personid= + id;
if
(selection !=
null
&& !.equals(selection.trim())) {
where = selection + and + where;
}
updatedNum = db.update(person, values, where, selectionArgs);
break
;
default
:
throw
new
IllegalArgumentException(unknown uri + uri.toString());
}
//向外界通知該ContentProvider里的數據發生了變化 ,以便ContentObserver作出相應
getContext().getContentResolver().notifyChange(uri,
null
);
return
updatedNum;
}
/**
* 刪除操作:
* 刪除操作有兩種可能:刪除一張表或者刪除某條數據
* 在刪除某條數據時原理類似於查詢某條數據,見下.
*/
@Override
public
int
delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
int
deletedNum =
0
;
switch
(mUriMatcher.match(uri)) {
// 刪除表
case
PERSON_DIR:
deletedNum = db.delete(person, selection, selectionArgs);
break
;
// 按照id刪除某條數據
case
PERSON:
long
id = ContentUris.parseId(uri);
String where = personid= + id;
if
(selection !=
null
&& !.equals(selection.trim())) {
where = selection + and + where;
}
deletedNum = db.delete(person, where, selectionArgs);
break
;
default
:
throw
new
IllegalArgumentException(unknown uri + uri.toString());
}
//向外界通知該ContentProvider里的數據發生了變化 ,以便ContentObserver作出相應
getContext().getContentResolver().notifyChange(uri,
null
);
return
deletedNum;
}
/**
* 查詢操作:
* 查詢操作有兩種可能:查詢一張表或者查詢某條數據
*
* 注意事項:
* 在查詢某條數據時要注意--因為此處是按照personid來查詢
* 某條數據,但是同時可能還有其他限制.例如:
* 要求personid為2且name為xiaoming1
* 所以在查詢時分為兩步:
* 第一步:
* 解析出personid放入where查詢條件
* 第二步:
* 判斷是否有其他限制(如name),若有則將其組拼到where查詢條件.
*
* 詳細代碼見下.
*/
@Override
public
Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
Cursor cursor =
null
;
switch
(mUriMatcher.match(uri)) {
// 查詢表
case
PERSON_DIR:
cursor = db.query(person, projection, selection, selectionArgs,
null
,
null
, sortOrder);
break
;
// 按照id查詢某條數據
case
PERSON:
// 第一步:
long
id = ContentUris.parseId(uri);
String where = personid= + id;
// 第二步:
if
(selection !=
null
&& !.equals(selection.trim())) {
where = selection + and + where;
}
cursor = db.query(person, projection, where, selectionArgs,
null
,
null
, sortOrder);
break
;
default
:
throw
new
IllegalArgumentException(unknown uri + uri.toString());
}
return
cursor;
}
}
|
SQLiteDatabaseOpenHelper如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package
cn.testcontentprovider;
import
android.content.Context;
import
android.database.sqlite.SQLiteDatabase;
import
android.database.sqlite.SQLiteOpenHelper;
public
class
SQLiteDatabaseOpenHelper
extends
SQLiteOpenHelper {
public
SQLiteDatabaseOpenHelper(Context context) {
super
(context, contentprovidertest.db,
null
,
1
);
}
@Override
public
void
onCreate(SQLiteDatabase db) {
db.execSQL(create table person(personid integer primary key autoincrement,name varchar(
20
),phone varchar(
12
),salary Integer(
12
)));
}
//當數據庫版本號發生變化時調用該方法
@Override
public
void
onUpgrade(SQLiteDatabase db,
int
arg1,
int
arg2) {
//db.execSQL(ALTER TABLE person ADD phone varchar(12) NULL);
//db.execSQL(ALTER TABLE person ADD salary Integer NULL);
}
}
|
MainActivity如下:
1
2
3
4
5
6
7
8
9
10
11
|
package
cn.testcontentprovider;
import
android.app.Activity;
import
android.os.Bundle;
public
class
MainActivity
extends
Activity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
|
AndroidManifest.xml如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!--?xml version=
1.0
encoding=utf-
8
?-->
<manifest android:versioncode=
"1"
android:versionname=
"1.0"
package
=
"cn.testcontentprovider"
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<uses-sdk android:minsdkversion=
"8"
android:targetsdkversion=
"8"
>
<uses-permission android:name=
"android.permission.INTERNET"
>
<uses-permission android:name=
"android.permission.ACCESS_NETWORK_STATE"
>
<uses-permission android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
>
<uses-permission android:name=
"android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
>
<intent-filter>
<category android:name=
"android.intent.category.LAUNCHER"
>
</category></action></intent-filter>
</activity>
<provider android:authorities=
"cn.bs.testcontentprovider"
android:exported=
"true"
android:name=
"cn.testcontentprovider.ContentProviderTest"
>
</provider></application>
</uses-permission></uses-permission></uses-permission></uses-permission></uses-sdk></manifest>
|
main.xml如下:
1
|
<relativelayout android:layout_height=
"match_parent"
android:layout_width=
"match_parent"
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
><button android:layout_centerinparent=
"true"
android:layout_height=
"wrap_content"
android:layout_width=
"wrap_content"
android:text=
"該應用包含一個自定義的ContentProvider"
android:textsize=
"15sp"
></button></relativelayout>
|
以下代碼為工程TestBaidu
MainActivity如下:
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
|
package
cn.testbaidu;
import
android.net.Uri;
import
android.os.Bundle;
import
android.os.Handler;
import
android.view.View;
import
android.view.View.OnClickListener;
import
android.widget.Button;
import
android.app.Activity;
import
android.content.ContentResolver;
import
android.content.ContentValues;
import
android.database.ContentObserver;
import
android.database.Cursor;
/**
* Demo描述:
* 應用A(TestBaidu)調用另外一個應用(TestContentProvider)中的自定義ContentProvider,即:
* 1 自定義ContentProvider的使用
* 2 其它應用調用該ContentProvider
* 3 ContentObserver的使用
*
* 備注說明:
* 1 該例子在以前版本的基礎上整理了代碼
* 2 該例子在以前版本的基礎上融合了ContentObserver的使用
* 利用ContentObserver隨時監聽ContentProvider的數據變化.
* 為實現該功能需要在自定義的ContentProvider的insert(),update(),delete()
* 方法中調用getContext().getContentResolver().notifyChange(uri, null);
* 向外界通知該ContentProvider里的數據發生了變化 ,以便ContentObserver作出相應
*
* 測試方法:
* 1 依次測試ContentProvider的增查刪改(注意該順序)!!
* 2 其它應用查詢該ContentProvider的數據
*
*/
public
class
MainActivity
extends
Activity {
private
Button mAddButton;
private
Button mDeleteButton;
private
Button mUpdateButton;
private
Button mQueryButton;
private
Button mTypeButton;
private
long
lastTime=
0
;
private
ContentResolver mContentResolver;
private
ContentObserverSubClass mContentObserverSubClass;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
initContentObserver();
}
private
void
init() {
mContentResolver=
this
.getContentResolver();
mAddButton=(Button) findViewById(R.id.addButton);
mAddButton.setOnClickListener(
new
ClickListenerImpl());
mDeleteButton=(Button) findViewById(R.id.deleteButton);
mDeleteButton.setOnClickListener(
new
ClickListenerImpl());
mUpdateButton=(Button) findViewById(R.id.updateButton);
mUpdateButton.setOnClickListener(
new
ClickListenerImpl());
mQueryButton=(Button) findViewById(R.id.queryButton);
mQueryButton.setOnClickListener(
new
ClickListenerImpl());
mTypeButton=(Button) findViewById(R.id.typeButton);
mTypeButton.setOnClickListener(
new
ClickListenerImpl());
}
// 注冊一個針對ContentProvider的ContentObserver用來觀察內容提供者的數據變化
private
void
initContentObserver() {
Uri uri = Uri.parse(content:
//cn.bs.testcontentprovider/person);
mContentObserverSubClass=
new
ContentObserverSubClass(
new
Handler());
this
.getContentResolver().registerContentObserver(uri,
true
,mContentObserverSubClass);
}
@Override
protected
void
onDestroy() {
super
.onDestroy();
if
(mContentObserverSubClass!=
null
) {
this
.getContentResolver().unregisterContentObserver(mContentObserverSubClass);
}
}
// 自定義一個內容觀察者ContentObserver
private
class
ContentObserverSubClass
extends
ContentObserver {
public
ContentObserverSubClass(Handler handler) {
super
(handler);
}
//采用時間戳避免多次調用onChange( )
@Override
public
void
onChange(
boolean
selfChange) {
super
.onChange(selfChange);
System.out.println(ContentObserver onChange() selfChange=+ selfChange);
if
(System.currentTimeMillis()-lastTime>
2000
) {
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse(content:
//cn.bs.testcontentprovider/person);
// 獲取最新的一條數據
Cursor cursor = resolver.query(uri,
null
,
null
,
null
,personid desc limit
1
);
while
(cursor.moveToNext()) {
int
personid = cursor.getInt(cursor.getColumnIndex(personid));
System.out.println(內容提供者中的數據發生變化,現數據中第一條數據的personid=+ personid);
}
cursor.close();
lastTime=System.currentTimeMillis();
}
else
{
System.out.println(時間間隔過短,忽略此次更新);
}
}
@Override
public
boolean
deliverSelfNotifications() {
return
true
;
}
}
private
class
ClickListenerImpl
implements
OnClickListener {
@Override
public
void
onClick(View v) {
switch
(v.getId()) {
case
R.id.addButton:
Person person =
null
;
for
(
int
i =
0
; i <
5
; i++) {
person =
new
Person(xiaoming + i,
9527
+ i, (
8888
+ i));
testInsert(person);
}
break
;
case
R.id.deleteButton:
testDelete(
1
);
break
;
case
R.id.updateButton:
testUpdate(
3
);
break
;
case
R.id.queryButton:
// 查詢表
// queryFromContentProvider(-1);
// 查詢personid=2的數據
testQuery(
2
);
break
;
case
R.id.typeButton:
testType();
break
;
default
:
break
;
}
}
}
private
void
testInsert(Person person) {
ContentValues contentValues=
new
ContentValues();
contentValues.put(name, person.getName());
contentValues.put(phone, person.getPhone());
contentValues.put(salary,person.getSalary());
Uri insertUri=Uri.parse(content:
//cn.bs.testcontentprovider/person);
Uri returnUri=mContentResolver.insert(insertUri, contentValues);
System.out.println(新增數據:returnUri=+returnUri);
}
private
void
testDelete(
int
index){
Uri uri=Uri.parse(content:
//cn.bs.testcontentprovider/person/+String.valueOf(index));
mContentResolver.delete(uri,
null
,
null
);
}
private
void
testUpdate(
int
index){
Uri uri=Uri.parse(content:
//cn.bs.testcontentprovider/person/+String.valueOf(index));
ContentValues values=
new
ContentValues();
values.put(name, hanmeimei);
values.put(phone,
1234
);
values.put(salary,
333
);
mContentResolver.update(uri, values,
null
,
null
);
}
private
void
testQuery(
int
index) {
Uri uri=
null
;
if
(index<=
0
) {
//查詢表
uri=Uri.parse(content:
//cn.bs.testcontentprovider/person);
}
else
{
//按照id查詢某條數據
uri=Uri.parse(content:
//cn.bs.testcontentprovider/person/+String.valueOf(index));
}
//對應上面的:查詢表
//Cursor cursor= mContentResolver.query(uri, null, null, null, null);
//對應上面的:查詢personid=2的數據
//注意:因為name是varchar字段的,所以應該寫作name='xiaoming1'
// 若寫成name=xiaoming1查詢時會報錯
Cursor cursor= mContentResolver.query(uri,
null
, name=
'xiaoming1'
,
null
,
null
);
while
(cursor.moveToNext()){
int
personid=cursor.getInt(cursor.getColumnIndex(personid));
String name=cursor.getString(cursor.getColumnIndex(name));
String phone=cursor.getString(cursor.getColumnIndex(phone));
int
salary=cursor.getInt(cursor.getColumnIndex(salary));
System.out.println(查詢得到:personid= + personid+,name=+name+,phone=+phone+,salary=+salary);
}
cursor.close();
}
private
void
testType(){
Uri dirUri=Uri.parse(content:
//cn.bs.testcontentprovider/person);
String dirType=mContentResolver.getType(dirUri);
System.out.println(dirType:+dirType);
Uri itemUri=Uri.parse(content:
//cn.bs.testcontentprovider/person/3);
String itemType=mContentResolver.getType(itemUri);
System.out.println(itemType:+itemType);
}
}
|
Person如下:
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
|
package
cn.testbaidu;
public
class
Person {
private
Integer id;
private
String name;
private
String phone;
private
Integer salary;
public
Person(String name, String phone,Integer salary) {
this
.name = name;
this
.phone = phone;
this
.salary=salary;
}
public
Person(Integer id, String name, String phone,Integer salary) {
this
.id = id;
this
.name = name;
this
.phone = phone;
this
.salary=salary;
}
public
Integer getId() {
return
id;
}
public
void
setId(Integer id) {
this
.id = id;
}
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
String getPhone() {
return
phone;
}
public
void
setPhone(String phone) {
this
.phone = phone;
}
public
Integer getSalary() {
return
salary;
}
public
void
setSalary(Integer salary) {
this
.salary = salary;
}
@Override
public
String toString() {
return
Person [id= + id + , name= + name + , phone= + phone+ , salary= + salary + ];
}
}
|