Android组件---ContentProvider
1.定义与作用
四大组件之一,内容提供者,使用内容提供器是目前Android中实现跨进程共享数据的标准方式。
进程间进行数据交互&共享,即跨进程通信。
注意:
1)ContentProvider = 中间者角色,真正存储和操作的数据源还是原来存储数据的方式(数据库、文件、xml或网络)
2)数据源:数据库、文件、网络、XML等等
2.用法简介
内容提供器的用法一般有两种:
1.使用现有的内容提供器来读取和操作相应程序中的数据
2.创建自己的内容提供器给我们应用程序的数据提供外部访问接口。
3.使用ContentResolver而非直接访问ContentProvider?
即:统一管理不同ContentProvider间的操作
1)通过URI即可操作不同的ContentProvider中的数据
2)外部进程通过ContentResolver类从而和ContentProvider类进行交互
原因:一款应用要使用多个ContentProvider,若需要了解每个ContentProvider的不同实现从而再完成数据交互,操作成本高且难度大。
在ContentProvider类上多加一个ContentResolver类,便于对所有的ContentProvider进行统一管理。
4.访问其它程序中的数据
ContentResolver contentResolver = getContentResolver();
或者
ContentResolver contentResolver = context.getContentResolver();
内容URI标准格式写法:
content:
content:
通配符*与#
1)*:表示匹配任意长度的任意字符
2)#:表示匹配任意长度的数字
所以,一个能匹配任意表的内容URI格式就可以写成:
content:
而一个能够匹配table表中任意一行数据的内容URI格式就可以写成:
content:
格式解析:
内容URI给内容提供器中的数据建立了唯一标识符,由权限和路径组成;
1)内容协议:协议声明,如上述中的content:
2)权限:用于对不同的程序作区分的,为了避免冲突,都会使用程序包名的方式命名,如上述的:com.excample.app.provider
3)路径:用于对同一应用程序中不同的表做区分的,通常会添加在权限的后面,例如table1和table2.
解析:(解析的关键点在于Uri.parse方法)
Uri uri = Uri.parse("content://com.excample.app.provider/table1");
1)查询table1表中的内容
Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
if(cursor != null){
while(cursor.moveToNext()){
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
2)向table1中增加数据
ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri,values);
3)更新table1中的数据
ContentValues values = new ContentValues();
values.put("column1","");
getContentResolver().update(uri,values,"column1=? and column2=?",new String[]{"text","1"});
4)删除table1中的数据
getContentResolver().delete(uri,"column2 = ?",new String[]{"1"});
5.内容URI对应的MIME
一个内容URI对应的MIME字符串主要由三个部分组成,Android对这三个部分做了如下规定:
1.必须以vnd开头
2.如果内容URI以路径结尾,则后接android.cursor.dir/,
如果内容URI以id结尾,则后接android.cursor.item/
3.最后接上vnd.<authority>.path
所以,对于content:
vnd.android.cursor.dir/vnd.com.excample.app.provider.table1
对于content:
vnd.android.cursor.item/vnd.com.excample.app.provider.table1
6.进程内通信
// 步骤:
1) 创建数据库类,增加相对应的增删改查逻辑
2)自定义ContentProvider类,重写对应方法
3)在AndroidManifest文件中注册自定义的ContentProvider
4)进程内访问ContentProvider
// 实例参考:(5.1部分)
https://blog.csdn.net/xyl826/article/details/100974149
7.进程间通信
// 思路:
进程1:创建ContentProvider,存储数据(采用SQLite)
进程2:访问ContentProvider中存储的数据
// 具体步骤
进程1:
1)创建数据库类
2)自定义ContentProvider
3) 在AndroidManifest文件中注册创建的ContentProvider类
进程2:
1)在AndroidManifest文件中声明可访问的权限(声明的权限必须与进程1中设置的权限对应)
2)访问ContentProvider的类
// 实例参考:(5.2部分)
https://blog.csdn.net/xyl826/article/details/100974149
8.辅助ContentProvider的工具类
作用:操作URI
核心方法:withAppendedId、parseId
示例1:ContentUris.withAppendedId
Uri uri = Uri.parse("content://com.excample.app.provider/user");
Uri resultUri = ContentUris.withAppendedId(uri,7);
生成的Uri为:content:
示例2:ContentUris.parseId
Uri uri = Uri.parse("content://com.excample.app.provider/user/7");
long personId = ContentUris.parseId(uri);
获取的结果为:7
作用:
1)在ContentProvider中注册URI
2)根据URI匹配ConentProvider中对应的数据表
具体使用:
1)初始化UriMatcher对象
UriMatcher matcher = new UriMathcer(UriMathcer.NO_MATCH);
注:常量UriMatcher.NO_MATCH = 不匹配任何路径的返回码,即初始化的时候不匹配任何东西
2)在ContentProvider中注册URI(add URI)
int URI_CODE_a = 1;
int URI_CODE_b = 2;
matcher.addURI("com.excample.provider","user1",URI_CODE_a);
matcher.addURI("com.excample.provider","user2",URI_CODE_b);
3)根据URI匹配URI_CODE,从而匹配ContentProvider中相应的资源(match方法)
@Override
public String getType(Uri uri){
Uri uri = Uri.parse("content://com.excample.provider/user1");
switch(matcher.match(uri)){
case URI_CODE_a:
return tableNameUser1;
case URI_CODE_b:
return tableNameUser2;
}
}
定义:内容观察者
作用:观察Uri引起ContentProvider中的数据变化并且通知外界(即访问该数据的访问者)
核心概念:当ContentProvider中的数据发生变化(增、删、改时,就会触发该ContentObserver类)
具体使用:
1)注册内容观察者ContentObserver
getContentResolver().registerContentObserver(uri);
2)当ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extend ContentProvider{
public Uri insert(Uri uri,ContentValues values){
db.insert("user","userId",values);
getContext().getContentResolver().notifyChange(uri,null);
}
}
3)解除观察者
getContentResolver().unregisterContentObserver(uri);
9.优点
ContentProvider为应用间的数据交互提供了一个安全的环境
相对于其他对外共享数据的方式,数据访问方式会因数据存储的方式而不同,这使得访问数据变得复杂且难度大;
1)采用文件方式对外共享数据,需要进行文件操作读写数据
2)采用SharedPreferences共享数据,需要使用SharedPr
eference API读写数据
而ContentProvider方式,其解耦了底层数据的存储方式,使得无论底层数据存储何种方式,外界对数据的访问方式都是统一的,
这使得访问简单且高效。
例如:项目刚开始采用SQLite数据库,后来把数据库换为GreenDao,也不会对上层数据ContentProvider使用代码产生影响。