Android-ContentProvider

Android-ContentProvider

ContentProvider 使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过 ContentResolver 类从该内容提供者中获取或存入数据。

只有需要在多个应用程序间共享数据是才需要内容提供者,且必须存储在一个内容提供者中。好处是可以统一数据访问方式。例如通讯录数据被多个应用程序使用。

ContentProvider 使用 URI(Universal Resource Identifier,统一资源定位符)来唯一标识其数据集,URI 以 content:// 作为前缀。通常不会直接使用 ContentProvider 类的对象,大多数是通过 ContentResolver 对象实现对 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
public class DemoContentProvider extends ContentProvider {
private static final String AUTHORITY = "luis.demo.DemoContentProvider";

// 匹配成功后返回的匹配码
private static final int MATCH_CODE = 100;

// 用于判断数据请求的 URI 是否匹配
private static UriMatcher uriMatcher;

// 该 APP 用于共享的数据集
private DemoData demoData;

// 数据改变后通知该 URI
private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/data");

static {
// 若匹配不成功则返回 NO_MATCH == -1
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

//添加预留需要匹配的 URI
uriMatcher.addURI(AUTHORITY, "data", MATCH_CODE);
}

@Override
public boolean onCreate() {
demoData = DemoData.getInstance();
return false;
}

@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder) {
// 执行数据的增删改查前先验证请求的 URI 和预留的 URI 是否匹配
if (uriMatcher.match(uri) == MATCH_CODE){
Cursor cursor = demoData.queryData();
return cursor;
}
return null;
}

@Nullable
@Override
public String getType(@NonNull Uri uri) { return null; }

@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
if (uriMatcher.match(uri) == MATCH_CODE){
demoData.insertData(values);
notifyChange();
}
return null;
}

@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
if (uriMatcher.match(uri) == MATCH_CODE){
int deleteCount = demoData.deleteData();
notifyChange();
return deleteCount;
}
return 0;
}

@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values,
@Nullable String selection, @Nullable String[] selectionArgs) {
if (uriMatcher.match(uri) == MATCH_CODE){
int updateCount = demoData.updateData();
notifyChange();
return updateCount;
}
return 0;
}

private void notifyChange(){
getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
}
}

在其他应用中使用 ContentResolver 获取数据:

1
ContentResolver contentResolver = getContentObserver();

非必须的可选功能:如果需要对 URI 指向的数据监听变化,则需要重写并注册 ContentObserver,并传入一个 Handler 以在数据发生变化时通过 Handler 通知主线程更新 UI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DemoContentProvider {
private Handler Handler;
public MyContentObserver(Handler handler) {
super(handler);
this.Handler = handler;
}

@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}

@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Message message = Message.obtain();
message.obj = uri;
Handler.sendMessage(message);
}
}

并且在需要接收数据的 ContentResolver 中注册该 ContentObserver:

1
2
3
private static final String AUTHORITY = "luis.demo.DemoContentProvider";
private static final Uri DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
contentResolver.registerContentObserver(DATA_URI, true, new DemoContentObserver(handler));

当需要对某个 ContentProvider 提供的数据增删改查时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 查询
Cursor cursor = contentResolver.query(DATA_URI, null, null, null, null, null);
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
dataValue1 = cursor.getXXX(cursor.getColumnIndex("column1"));
dataValue2 = cursor.getXXX(cursor.getColumnIndex("column2"));
......
}

// 插入
ContentValues contentValues = new ContentValues();
contentValues.put("culumn1", dataValue1);
contentValues.put("culumn2", dataValue2);
......
contentResolver.insert(DATA_URI, contentValues);

// 删除
contentResolver.delete(DATA_URI, null, null);

可以看出,对一个 App 共享的数据集,通常是通过 ContentResolver 处理的,而 ContentProvider 更像是提供了一个可以处理数据的接口,其定义了入口 URI,调用增删改查时的具体逻辑,并返回数据处理的结果。