前言
ContentProvide是四大组件之一,说明他是很重要的,虽然项目中不经常使用,但是还是需要学一下的
什么是ContentProvider
ContentProvider为不同的应用之间实现数据共享,提供统一的接口,也就是说ContentProvider可以实现进程间的数据共享,实现跨进程通信
为什么用ContentProvider
- ContentProvider提供了对底层数据存储方式的抽象,比如下图,底层使用了Sqlit数据库,在用ContentProvider进行封装后,把sqlit换成其他数据库也不会影响工功能
- ContentProvider为应用间的数据交互提供了一个安全的环境
怎么使用ContentProvider
首先我们需要知道三个类
- ContentProvider(内容提供者)
- ContentResolver(内容解析者)
- ContentObserver(内容观察者)
假如我们现在有个应用A 提供了数据 ,应用B要操作应用A的数据,那么
- 应用A使用ContentProvider去共享自己数据
- 应用B使用ContentResolver去操作应用A的数据,通过ContentObserver去监听应用A的数据变化
- 当应用A的数据发送改变时,通知ContentObserver去告诉应用B数据变化了及时更新
这就是通信的大致流程,在了解更加详细的流程之前,我们还需要知道几个概念
ContentProvider中的URI
ContentProvider中的URI是有固定格式的,例如:
Authority:授权信息,以区别不同的ContentProvider
path:表名,以区分ContentProvider中不同的数据表
id:id号,用于区分表中的不同数据
- getAuthority():获取Uri中Authority部分
- getPath():获取Uri中path部分
UriMatch
UriMatch主要为了区配URI,比如应用A提供了数据,但是并不是说有的应用都可以操作这些数据,只有提供了满足应用A的URI,才能访问A的数据,而UriMatch就是做这个区配工作的,他有如下方法
- public UriMatcher(int code) :它的作用就是创建一个UriMatch对象
- public void addURI(String authority,String path, int code):它的作用是在ContentProvider添加一个用于匹配的Uri,当匹配成功时返回code。Uri可以是精确的字符串,Uri中带有*表示可匹配任意text,#表示只能匹配数字。
- public int match(Uri uri) :这里的Uri就是传过来的要进行验证,匹配的Uri假如传过来的是:content://com.example.test/student/#,content://com.example.test/student/10可以匹配成功,这里的10可以使任意的数字。
实例
首先我们先完成应用A的工作,首先创建一个MyContentProvider继承ContentProvider
public class MyContentProvider extends ContentProvider {
//这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities,这里的authorities可以随便写
private static final String AUTHORITY = "com.example.student";
//匹配成功后的匹配码
private static final int MATCH_ALL_CODE = 1;
private static final int MATCH_ONE_CODE = 2;
private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/student");
private static final UriMatcher uriMatcher;
//在静态代码块中添加要匹配的 Uri
static {
//匹配不成功返回NO_MATCH(-1)
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* uriMatcher.addURI(authority, path, code); 其中
* authority:主机名(用于唯一标示一个ContentProvider,这个需要和清单文件中的authorities属性相同)
* path:路径路径(可以用来表示我们要操作的数据,路径的构建应根据业务而定)
* code:返回值(用于匹配uri的时候,作为匹配成功的返回值)
*/
uriMatcher.addURI(AUTHORITY, "student", MATCH_ALL_CODE);// 匹配记录集合
uriMatcher.addURI(AUTHORITY, "student/#", MATCH_ONE_CODE);// 匹配单条记录
}
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
switch (uriMatcher.match(uri)) {
/**
* 这里如果匹配是uriMatcher.addURI(AUTHORITY, "student",
* MATCH_SUCCESS_CODE);中的Uri,则查询全部
*
*/
case MATCH_ALL_CODE:
Log.d("mmm", "queryAll");
break;
/**
* 这里如果匹配是uriMatcher.addURI(AUTHORITY,
* "student/#",MATCH_ONE_CODE);中的Uri,查询一个
*/
case MATCH_ONE_CODE:
break;
default:
}
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
/**
* 插入 使用UriMatch的实例中的match方法对传过来的 Uri进行匹配。 这里通过ContentResolver传过来一个Uri,
* 用这个传过来的Uri跟在ContentProvider中静态代码块中uriMatcher.addURI加入的Uri进行匹配
* 根据匹配的是否成功会返回相应的值,在上述静态代码块中调用uriMatcher.addURI(AUTHORITY,
* "student",MATCH_CODE)这里的MATCH_CODE
* 就是匹配成功的返回值,也就是说假如返回了MATCH_CODE就表示这个Uri匹配成功了
* ,我们就可以按照我们的需求就行操作了,这里uriMatcher.addURI(AUTHORITY,
* "person/data",MATCH_CODE)加入的Uri为:
* content://com.example.studentProvider/student
* ,如果传过来的Uri跟这个Uri能够匹配成功,就会按照我们设定的步骤去执行相应的操作
*/
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
switch (uriMatcher.match(uri)) {
case MATCH_ALL_CODE:
String aaa = (String) values.get("aaa");
Log.d("mmm", "insertAll" + aaa);
//通知ContentObserver数据发生变化了
notifyDataChanged();
break;
case MATCH_ONE_CODE:
break;
default:
}
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
case MATCH_ALL_CODE:
Log.d("mmm", "deleteAll");
break;
case MATCH_ONE_CODE:
break;
default:
}
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
case MATCH_ALL_CODE:
Log.d("mmm", "updateAll");
break;
case MATCH_ONE_CODE:
break;
default:
}
return 0;
}
//通知指定URI数据已改变
private void notifyDataChanged() {
getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
}
}
因为ContentProvider作为四大组件之一,所以还需要在AndroidManifest.xml中注册一下。
<provider
android:authorities="com.example.student"
android:name=".MyContentProvider"
android:exported="true"/>
这里的authorities就是它是唯一标识内容提供者的,为内容提供者指定一个唯一的标识,这样别的应用才可以唯一获取此provider,exported的值为[flase|true]当为true时:当前提供者可以被其它应用使用。
现在应用A已经完成了,下面我们看下应用B怎么写
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities,这里的authorities可以随便写
private static final String AUTHORITY = "com.example.student";
private static final Uri URI = Uri.parse("content://" + AUTHORITY + "/student");
private ContentResolver contentResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initData() {
contentResolver = getContentResolver();
//注册内容观察者
contentResolver.registerContentObserver(URI, true, new MyContentObserver(null));
}
private void initView() {
findViewById(R.id.zeng).setOnClickListener(this);
findViewById(R.id.shan).setOnClickListener(this);
findViewById(R.id.gai).setOnClickListener(this);
findViewById(R.id.cha).setOnClickListener(this);
}
@Override
public void onClick(View v) {
ContentValues contentValues = new ContentValues();
contentValues.put("aaa", "aaaa");
//通过内容解析者操作内容观察者
switch (v.getId()) {
case R.id.zeng:
contentResolver.insert(URI, contentValues);
break;
case R.id.shan:
contentResolver.delete(URI, null, null);
break;
case R.id.gai:
contentResolver.update(URI, contentValues, null, null);
break;
case R.id.cha:
contentResolver.query(URI, null, null, null, null);
break;
}
}
//定义内容观察者
class MyContentObserver extends ContentObserver {
public MyContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d("mmm", "改变了");
}
}
}
然后操作增删改查,看下log
01-18 18:30:44.550 1987-2001/? D/mmm: insertAllaaaa
01-18 18:30:44.552 1955-1975/com.baidu.bpit.aibaidu.myapplication D/mmm: 改变了
01-18 18:30:46.194 1987-2001/com.baidu.bpit.aibaidu.service D/mmm: deleteAll
01-18 18:30:48.016 1987-2001/com.baidu.bpit.aibaidu.service D/mmm: updateAll
01-18 18:30:48.978 1987-2001/com.baidu.bpit.aibaidu.service D/mmm: queryAll
可以看到都通知到了应用A中,因为应用A的insert方法,通知了ContentObserver,所以应用B可以收到数据改变的通知
参考 https://www.jianshu.com/p/f5ec75a9cfea
https://blog.csdn.net/dmk877/article/details/50387741