ContentProvider

sky-mxc 总结 转载注明:https://sky-mxc.github.io

ContentProvider

内容提供者,应用程序间的数据交互,是为存储和获取数据提供的统一接口。
Contentprovider为应用间数据交互提供了安全的环境,它允许把自己的应用数据开放给其他应用进行 CRUD。怎么样进行操作可以自己规定,不用担心权限的问题。


当然如果不想被被人读取自己应用的数据就不需要这个内容提供者。


ContentResolver来访问和操作我们的数据。
ContentResolver 通过我们注册的uri就可以找到我们开放的数据。

关于uri 参考这篇文章:http://blog.csdn.net/dlutbrucezhang/article/details/8917303

创建一个ContentProvider

创建自己的内容提供程序 只需继承ContentProvider即可。
这里就以insert 和 query 为例

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
package com.skymxc.demo.contentprovider.util;
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;
import android.support.annotation.Nullable;
import android.text.TextUtils;
/**
* Created by sky-mxc
*/
public class StudentProvider extends ContentProvider {
private DBHelper dbHelper;
private UriMatcher uriMatcher;
//匹配结果是一张表
private static final int STUDENTS = 1;
//匹配结果是一个条数据
private static final int STUDENT = 2;
//一般是包名 避免重复
private static final String AUTHORITY = "com.skymxc.demo";
@Override
public boolean onCreate() {
dbHelper = new DBHelper(getContext());
//初始化 uri匹配者 UriMatcher.NO_MATCH:匹配不上时返回
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//添加能够匹配的uri格式 参数1authorities 匹配住机。 参数2 匹配路径 参数3 code : match success return this code;
// 代表这个uri 操作的是一个表,匹配码是 STUDENTS
uriMatcher.addURI(AUTHORITY,"student", STUDENTS);
//代表这个uri 操作的是一条数据 匹配成功后返回 STUDENT
uriMatcher.addURI(AUTHORITY,"student/#",STUDENT);
return true;
}
/**
* 查询操作
* @param uri
* @param projection 要查询的列
* @param condition 查询条件
* @param values 查询参数
* @param sortOrder 排序
* @return
*/
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String condition, String[] values, String sortOrder) {
Cursor cursor =null;
SQLiteDatabase db= dbHelper.getDB();
//匹配这个uri 要查询一张表还是 某条数据
switch (uriMatcher.match(uri)){
case STUDENT:
//查询某条数据 ContentUris :工具类 可以解析出id
long id= ContentUris.parseId(uri);
String where ="_id ="+id+" ";
if (!TextUtils.isEmpty(condition)){
where+= " and "+condition;
}
cursor= db.query(DBHelper.TABLE_NAME,projection,where,values,null,null,sortOrder);
break;
case STUDENTS:
//查询一张表
cursor = db.query(DBHelper.TABLE_NAME,projection,condition,values,null,null,sortOrder);
break;
default:
throw new IllegalArgumentException("match fail 。uri:"+uri+"");
}
return cursor;
}
@Nullable
@Override
public String getType(Uri uri) {
String type="unKnow";
switch (uriMatcher.match(uri)){
case STUDENT:
type="vnd.android.cursor.item/student";
break;
case STUDENTS:
type= "vnd.android.cursor.dir/student";
break;
}
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getDB();
switch (uriMatcher.match(uri)){
case STUDENT:
break;
case STUDENTS:
db.insert(DBHelper.TABLE_NAME,"_id",values);
break;
}
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}

创建完之后还需要在manifest文件中注册 才能被其他应用看到,通过 元素注册一个内容提供者

1
2
3
4
5
6
7
8
9
10
11
12
<!--
android:exported 设置此provider是否可以被其他应用使用。
android:readPermission 该provider的读权限的标识
android:writePermission 该provider的写权限标识
android:permission provider读写权限标识
android:grantUriPermissions 临时权限标识
-->
<provider
android:authorities="com.skymxc.demo"
android:name=".util.StudentProvider"
android:exported="true"/>

关于临时权限标识 grantUriPermissions :true时,意味着该provider下所有数据均可被临时使用;false时,则反之,但可以通过设置标签来指定哪些路径可以被临时使用。这么说可能还是不容易理解,我们举个例子,比如你开发了一个邮箱应用,其中含有附件需要第三方应用打开,但第三方应用又没有向你申请该附件的读权限,但如果你设置了此标签,则可以在start第三方应用时,传入FLAG_GRANT_READ_URI_PERMISSION或FLAG_GRANT_WRITE_URI_PERMISSION来让第三方应用临时具有读写该数据的权限。

到这里 一个简单的内容提供者就创建完成了


ContentResolver

可以看做是客户端 与ContentProvider 对应 ,ContentProvider 负责提供数据操作接口 ,ContentResolver 可以调用ContentProvider的数据接口对数据进行操作

为了测试上面定义的ContentProvider ,另创建一个Module 进行读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void read() {
ContentResolver resolver= getContentResolver() ;
String uriStr ="content://com.skymxc.demo/student";
Cursor cursor= resolver.query(Uri.parse(uriStr),new String[]{"_id","name","age"},null,null,"age");
StringBuffer sb = new StringBuffer("============student==================\n");
while (cursor !=null &&cursor.moveToNext()){
long id = cursor.getLong(cursor.getColumnIndex("_id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
sb.append("==="+id+"===\n");
sb.append("name:"+name+"\n");
sb.append("age:"+age+"\n");
}
sb.append("============================");
tv.setText(sb.toString());
if (cursor != null){
cursor.close();
}
}

insert

1
2
3
4
5
6
7
8
9
10
11
12
private void insert() {
String name = etName.getText().toString();
String age = etAge .getText().toString();
ContentResolver resolver = getContentResolver();
String uriStr="content://com.skymxc.demo/student";
ContentValues cv = new ContentValues();
cv.put("name",name);
cv.put("age",age);
resolver.insert(Uri.parse(uriStr),cv);
}

ContentResolver 还可以用来操作 短信,联系人,多媒体等 数据,这里写个读取短信的实例

读取短信的权限

1
2
><uses-permission android:name="android.permission.READ_SMS"/>
>`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 短信查询
*/
private void querySms() {
String[] projection = new String[]{"_id","address","person","body","type"};
StringBuffer sb = new StringBuffer("短信数据=============\n");
ContentResolver resolver= getContentResolver();
Cursor cursor = resolver.query(Uri.parse("content://sms/"),projection,null,null,null);
while (cursor != null && cursor.moveToNext()){
sb.append("id:"+cursor.getInt(cursor.getColumnIndex("_id")));
sb.append("\naddress:"+cursor.getString(cursor.getColumnIndex("address")));
sb.append("\nperson:"+cursor.getString(cursor.getColumnIndex("person")));
sb.append("\nbody:"+cursor.getString(cursor.getColumnIndex("body")));
sb.append("\ntype:"+cursor.getString(cursor.getColumnIndex("type")));
sb.append("\n=================================================");
}
tv.setText(sb.toString());
}

ContentObserver

内容观察者,可以给某些数据注册观察者,当数据改变时做出有些操作

初始化观察者

1
2
3
4
5
6
7
8
private ContentObserver observer = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.e("MainActivity","======数据改变了===");
}
};
`

注册观察者

1
2
3
4
5
6
7
8
9
Uri uri = Uri.parse("content://"+StudentProvider.AUTHORITY+"/student");
//为student 注册观察者
/**
* parameter1 观察的uri
* parameter2 uri的后代是否连带 观察
* parameter3 observer
*/
getContentResolver().registerContentObserver(uri,true,observer);

内容改变时 通知观察者

系统会首先查找 uri 扫描(手机上)所有的注册的observer 的uri 匹配之后执行 observer的onChange 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getDB();
int line=0;
switch (uriMatcher.match(uri)){
case STUDENT:
break;
case STUDENTS:
line= (int) db.insert(DBHelper.TABLE_NAME,"_id",values);
break;
}
if (line>0){
getContext().getContentResolver().notifyChange(uri,null);
}
return uri;
}

当在另一个应用插入数据时 change()调用

1
E/MainActivity: ======数据改变了===