Android 系列笔记 十 - 内容提供者

ContentProvider 内容提供者

作用:把私有数据暴露给其它应用,通常是把私有数据库的数据暴露给其它应用

ContentProvider作为安卓的四大组件之一,使用时首先要在配置清单文件中声明。
如果ContentProvider在清单文件中声明了权限,则同时要在配置清单中定义这个权限,同样,内容访问者要访问这个provider要首先声明相应的使用权限。

在application节点下添加以下内容:

1
2
3
4
5
6
7
<provider
//其它应用访问内容提供者的地址
android:authorities="自定义唯一识别码"
android:name="包名+类名"
android:exported="true" //若为false,则不允许其他应用访问
android:readPermission="自定义一"//读权限
android:writePermission="自定义二"/>//写权限

若provider声明了权限,则要在Manifest节点下定义相应的权限

1
2
<permission android:name="自定义一"/>
<permission android:name="自定义二"/>

自定义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
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
final static int INSERT = 1;
final static int DELETE = 2;
final static int UPDATE = 3;
final static int QUERYALL = 4;
final static int QUERYITEM = 5;
//自定义内容提供者唯一标识符
static String authorities = "com.example.myProvider";
static UriMatcher uriMatcher;
MyOpehHelper mOpenHelper;
SQLiteDatabase db;

static{
//检测其它用户传入的Uri与匹配器定义好的uri中,哪条匹配,不匹配返回 NO_MATCH
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH)
//向内容提供者添加访问的Uri,根据Uri的访问相应的数据。
//content://authorities/person/insert 相匹配的话 返回 INSERT
uriMatcher.addUri(authorities, "person/insert", INSERT);
uriMatcher.addUri(authorities, "person/delete", DELETE);
uriMatcher.addUri(authorities, "person/update", UPDATE);
uriMatcher.addUri(authorities, "person/queryAll", QUERYALL);
//# 可以代表任意一个数字,匹配Uri的话返回相应的匹配码
uriMatcher.addURI(authorities, "person/queryItem/#", QUERYITEM);
}

@Override
public boolean onCreate() {
mOpenHelper = new MyOpenHelper(getContext());
return true;
}

@Override
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
db = mOpenHelper.getWritableDatabase();
//使用Uri匹配器匹配传入的uri,匹配的话会返回定义好的匹配码
switch(uriMatcher.match(uri)){
case QUERYALL:
Cursor c = db.query("person", projection, selection,
selectionArgs, null, null, sortOrder);
return c;
case QUERYITEM://查询单条数据
if(db.isOpen()){
//把Uri末尾携带的数据取出来
long id = ContentUris.parseId(uri);
Cursor cursor = db.query("person", projection, "_id=?",
new String[]{id + ""}, null, null, sortOrder);
return cursor;
}
return null;
default:
throw new RuntimeException("Uri not match");
}
}

@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
case QUERYALL:
return "vnd.android.cursor.dir/person";
case QUERYITEM:
return "vnd.android.cursor.item/person";
default:
break;
}
return null;
}

//此方法供其它应用调用,用于往数据库中插入数据
//values: 由其它应用传入,用于封装要插入的数据
//uri: 内容提供者的访问地址
@Override
public Uri insert(Uri uri, ContentValues values) {
db = mOpenHelper.getWritableDatabase();
//使用Uri匹配器匹配传入的uri,匹配的话会返回定义好的匹配码
switch(uriMatcher.match(uri)){
case INSERT:
long id = db.insert("person", null, values);
return ContentUris.withAppendedId(uri, id);
default:
throw new RuntimeException("Uri not match");
}
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs){
int i = db.delete("person", selection, selectionArgs);
return i;
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs){
int i = db.update("person", values, selection, selectionArgs);
return i;
}

ContentResolver 内容访问者(访问内容提供者)

若要访问的内容提供者需要权限,则要在配置文件中声明相应的权限。

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
private ContentResolver resolver;
private Uri uri;
//要访问的内容提供者的Uri前缀
String authorities = "content://com.example.myProvider";

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

resolver=getContentResolver();
//insert();
//delete();
//update();
queryAll();
queryItem();
}

public void insert(){
ContentValues values = new ContentValues();
values.put("name", "Jack");
values.put("money", "13000");
//url: 访问内容提供者的向数据库中插入数据的地址
resolver.insert(Uri.parse(authorities + "/person/insert"), values);;
}

public void delete(){
resolver.delete(Uri.parse("content://内容提供者的唯一标识符"), "name = ?",
new String[]{"Jack"});
}

public void update(){
ContentValues values = new ContentValues();
values.put("name", "hello");
resolver.update(Uri.parse("content://内容提供者的唯一标识符"), values,
"name = ?", new String[]{"Jack"});
}

public void queryAll(){
uri = Uri.parse("content://com.example.myProvider/person/queryAll");
Cursor c = resolver.query(uri,new String[]{"name", "age"},
null, null, "age desc");
if(c != null && c.getCount() > 0) {
while (c.moveToNext()) {
Log.i("query from resolver", c.getString(0) + " " + c.getInt(1));
}
c.close();
}
}

public void queryItem(){
uri = Uri.parse("content://com.example.myProvider/person/queryItem/#");
//在Uri末尾添加一个id,把Uri末尾的#替换为id
uri = ContentUris.withAppendedId(uri,1);
Cursor c = resolver.query(uri, new String[]{"_id", "name", "age"},
null, null, "age desc");
if(c != null && c.moveToFirst()) {
int id = c.getInt(0);
String name = c.getString(1);
int age = c.getInt(2);
Log.i("queryItem from reslover", id + " " + name + " " + age);
c.close();
}else {
Log.i("queryItem from reslover", "null");
}
}

备份短信

1.清单文件中添加读取短信的权限
2.定义一个短信的 Java Bean 类 Message

1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<Message> smsList = new ArrayList<Message>();
ContentResolver cr = getContentResolver();
Uri uri = Uri.parse("content://sms");
Cursor cursor = cr.query(uri, new String[]{"address", "date", "body", "type"},
null, null, null);
while(cursor.moveToNext()){
String address = cursor.getString(0);
long date = cursor.getLong(1);
String body = cursor.getString(2);
String type = cursor.getString(3);

Message sms = new Message();
smsList.add(sms);
}

插入短信

清单文件中添加写短信与读短信的权限

1
2
3
4
5
6
7
8
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put("address", 23423);
values.put("type", 1);
values.put("date", System.currentTimeMillis());
values.put("body", "hello, hhhh");
Uri uri = Uri.parse("content://sms");
cr.insert(uri, valuse);

获取系统联系人

  1. raw_contacts 表
    contact_id: 联系人 id
  2. data 表
    存放联系人的详细信息,每行数据存放联系人单独的一条信息
    data1: 联系人具体信息
    raw_contact_id: 该行信息属于哪个联系人
    mimetype_id: 该行信息属于什么类型
    注意:实际查询的时候,并不能直接查询mimetype_id字段,而是查询mimetype字段
  3. mimetypes 表
    对应类型的字符串

清单文件中添加相应的权限

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
ContentResolver cr = getContentResolver();
String authorities = "content://com.android.contacts";
Cursor cursorContactId = cr.query(Uri.parse(authorities + "/raw_contacts"), new String[]{"contact_id"}, null, null, null);
while(cursorContactId.moveToNext()){
//获取联系人id
String contactId = cursorContactId.getString(0);
Cursor cursorData = cr.query(Uri.parse(authorities + "/data",
new String[]{"data1", "raw_contact_id", "mimetype"},
"raw_contact_id = ?", new String[]{contactId}, null);
//获取所有字段的名字
String[] names = cursorData.getColumnNames();

Person person = new Person();
whilw(cursorData.moveToNext()){
String data1 = cursorData.getString(0);
String mimetype = cursorData.getString(1);
if(mimetype.equals("vnd.android.cursor.item/email_v2")){
person.setEmail(data1);
}else if(mimetype.equals("vnd.android.cursor.item/phone_v2")){
person.setPhone(data1);
}
else if(mimetype.equals("vnd.android.cursor.item/name")){
person.setName(data1);
}
}
}

插入联系人

清单文件中添加相应的权限

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
ContentResolver cr = getContentResolver();
String authorities = "content://com.android.contacts";
//先查询raw_contacts表,获取最新联系人的主键,然后主键+1,就是要插入联系人的id
Cursor cursorContactId = cr.query(Uri.parse(authorities + "/raw_contacts"), new String[]{"contact_id"}, null, null, null);
//默认联系人id为1
int contact_id = 1;
while(cursorContactId.moveToLast()){
int _id = cursorContactId.getInt(0);
contact_id = ++_id;
}
ContentValues values = new ContentValues();
values.put("contact_id", contact_id);
cr.insert(Uri.parse(authorities + "/raw_contacts"), values);

values.clear();
values.put("data1", "Jack");
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/name");

values.clear();
values.put("data1", "[email protected]");
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/email_v2");

values.clear();
values.put("data1", "2453254532");
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
cr.insert(Uri.parse(authorities + "/data"), values);

注册内容观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ContentResolver cr = getContentResolver();
//notifyForDescendents: 如果是true,那么只要以 content://sms 开头的uri的数据改变,都能收到通知。
//比如 content://sms/inbox
cr.registerContentObserver(Uri.parse("content://sms"),
true, new MyObserver(new Handler()));

class MyObserver extends ContentObserver{
public MyObserver(Handler handler){
super(handler);
}

//收到数据改变的通知,调用此方法
@Override
public void onChange(boolean selfChange){
super.onChange(selfChange);
//读取短信数据库
}
}

内容提供者中发送数据改变的通知

插入、删除、更新等数据改变的操作,发送数据改变的通知

1
2
//uri: 通知发送到哪一个uri上,所有注册在这个uri上的内容观察者都可以收到这个通知
getContext().getContentResolver().notifyChange(uri, null);