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可以写成:
* content://cn.bs.testcontentprovider/*
* 一个能匹配person表中任意一行的Uri可以写成:
* content://cn.bs.testcontentprovider/person/#
*
*/
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 + ];
}
}
|