使用场景
ContentProvider可以在应用间共享数据,或者说ContentProvider可以把本应用的数据暴露给其他应用,提供有限的(应用自身可控制的)增删改查
使用方法
应用A,把数据暴露出去(为使代码简单明了,代码里删去具体数据库操作部分)
public class MyContentProvider extends ContentProvider{
private static final String AUTHORITY = "com.android.mycontentprovider";
private static final int ITEMS = 1;
private static final int ITEM_ID = 2;
private static final int ITEM = 3;
private ContentResolver resolver = null;
@Override
public boolean onCreate() {
resolver = getContext().getContentResolver();
return false;
}
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "item", ITEMS);//匹配所有item,例如查询则是查询所有item
uriMatcher.addURI(AUTHORITY, "item/#", ITEM_ID);//匹配单个item,id为xxx的item,例如删除则是删除id为xxx的item
uriMatcher.addURI(AUTHORITY, "item/#", ITEM);//匹配单个item,例如插入一个item
}
/**
* 返回对应的内容类型
* 如果返回集合的内容类型,必须以vnd.android.cursor.dir开头
* 如果是单个元素,必须以vnd.android.cursor.item开头
*/
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch(uriMatcher.match(uri)){
case ITEM_ID:
return "vnd.android.cursor.item/person";
case ITEMS:
return "vnd.android.cursor.dir/person";
default:
throw new IllegalArgumentException("this is unkown uri:" + uri);
}
}
/*
* 根据匹配值做查询并返回结果集
* */
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
switch (uriMatcher.match(uri)){
case ITEM_ID://查询id为xxx的ITEM
String id = uri.getPathSegments().get(1);//查询第一个参数即id
break;
case ITEMS://查询所有item
break;
}
SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
Cursor cursor = sqlBuilder.query(null,null,null,null,null,null,null);//假设这里做了相对的查询
cursor.setNotificationUri(resolver,uri);
return cursor;
}
/*
* 根据匹配值做删除数据库操作并按要求返回Uri
* */
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Uri insertUri = null;
switch(uriMatcher.match(uri)){
case ITEM:
//做插入数据values里有每个字段的值
int id = 0;//假设这个id为插入后返回的id值
insertUri = ContentUris.withAppendedId(uri, id);
break;
}
return insertUri;
}
/*
* 根据匹配值做删除操作并返回是否删除成功
* */
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
/*
* 根据匹配值做更新操作并返回是否更新成功
* */
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
在AndroidManifest.xml里注册,android:authorities是其他应用能找到我们这个ContentProvider的凭据
<provider android:name=".MyContentProvider" android:authorities="com.android.mycontentprovider" />
应用B,访问应用A的数据
ContentResolver mContentResolver = context.getContentResolver();
//content: 这部分是android定义的。ContentProvider专属,表示数据由ContentProvider提供
//com.android.mycontentprovider这就是我们的凭证了,要跟AndroidManifest.xml里配置的对上号
//item这部分就是匹配用的了
Uri uri = Uri.parse("content://com.android.mycontentprovider/item");
ContentValues values = new ContentValues();
values.put("name","huangshunbo");
values.put("age",19);
mContentResolver.insert(uri,values);//插入一条数据name=huangshunbo,age=19
uri = Uri.parse("content://com.android.mycontentprovider/item/1");
mContentResolver.delete(uri,"name = ?",new String[]{"huangshunbo"});//将id=1,name=huangshunbo的记录的删除
mContentResolver.update(uri,values,null,null);//更新数据
mContentResolver.query(uri,new String[]{"name"},null,null,null);//查询id为1的数据并只返回name字段
权限与数据监听
ContentProvider是可以进行权限设置的,就是说如果应用A设置了权限,那么应用B想要获取应用A的数据或者修改A的数据需要在AndroidManifest.xml中进行权限申请。具体如下
应用A可以设置android:permission/android:readPermission/android:writePermission三个权限。第一个的优先级最低,如没有设置第二第三个权限则拥有第一个权限即可操作读写。另外android:exported是否暴露自身,true则第三方应用可以访问到,false则只有自身或用户id一样的应用方可访问到
<provider
provider android:name=".MyContentProvider"
android:authorities="com.android.mycontentprovider"
android:exported="true"
android:permission="com.android.mycontentprovider"
android:readPermission="com.android.mycontentprovider.read"
android:writePermission="com.android.mycontentprovider.write"/>
然后com.android.mycontentprovider等的字符串目前是没有意义的,需在application标签的同级目录,写上申请permission的代码
<permission
android:name="com.android.mycontentprovider"
android:label="provider pomission"
android:protectionLevel="normal" />
这样应用A的权限控制就设置好了,应用B想访问的话就必须申请权限了
<uses-permission android:name="com.android.mycontentprovider"/>
数据监听 这个就简单了,就是个观察者模式,首先需要在应用A这边新建一个类继承ContentObserver,数据变化会通知到这里。然后注册
getContentResolver().registerContentObserver(CONTENT_URI_FIRST,true,observer);
不用的时候记得注销
getContentResolver().unregisterContentObserver(observer);
最后每当应用B这边数据变动时调用如下通知众观察者
getContext().getContentResolver().notifyChange(uri, null);
源码
待续。。。
杂
- 说说 ContentProvider、ContentResolver、ContentObserver 之间的关系
a. ContentProvider 内容提供者,用于对外提供数据
b. ContentResolver 用于获取ContentProvider提供的数据,注册ContentObserver监听,提醒数据变化
c. ContentObserver 监听器,可以监听数据的改变