Content Provider是什么
今天我们来学习Android的一个核心组件—Content Provider。在Android系统中,为了安全考虑,一般情况下一个进程是无法直接访问另外一个进程的内存的,原因是Android是一个多进程系统,在这个系统中,应用程序(或者系统的部分)会在自己的进程中运行。系统和应用之间的安全性通过Linux的facilities在进程级别来强制实现的,比如会给应用程序分配user ID和Group ID。更细化的安全特性是通过”Permission”机制对特定的进程的特定的操作进行限制,而”per-URI permissions”可以对获取特定数据的access专门权限进行限制。 所以,应用程序之间的一般是不可以互相访问的,因此,Android中一般也是不允许一个进程直接访问另外一个进程的私有数据(SharedPreference文件中的数据,SQLite数据库中的数据)。但在有些情况,应用程序需要将自己的某些数据暴露出来以供其它应用程序访问使用(比如手机中的联系人信息,音视频、图片资源等等),那么Android为了满足此需求,于是创建了Content Provider。在Android系统中,存在着许多 Content Provider集合,作为应用程序中的一部分,它们封装了数据,并为数据提供了安全的访问机制,但它们最主要的作用还是支持在多个应用中存储和读取数据,实现了应用程序间数据的共享。总之对Content Provider的定义一句话概括就是:Content Provider是Android为实现应用程序间安全地共享和访问数据所提供的一个持久的标准接口。
ContentResolver
Content Provider一般有两种用法,一种是使用现有的ContentProvider来读取或更新系统的数据,比如联系人信息的读取,还有一种就是自己创建ContentProvider提供接口供给外部应用访问。首先来了解第一种吧!
对于每一个应用程序想要访问系统数据,就一定要借用ContentResolver类,该类是提供给应用去访问其他应用内容的,我们可以通过Context中的getContentResolver()获取其实例,它提供了一系列的方法用于CRUD操作。
不同于SQLiteDatabase,ContentResolver中增删查改数据是通过uri代替的,内容uri给内容提供者中的数据建立了一个唯一的标识,它主要包括两个部分,权限(authority)和路径(path),权限我们一般采用应用程序的包命名,比如com.example.app,那么该程序的对应的权限可以命名为com.example.app.provider,路径则一般使用同一应用程序的不同表名做区分,比如table1和table2,此时内容uri就变成com.example.app.provider/table1,最后我们需要在内容uri中加上协议声明,此时标准的内容uri就变成
content://com.example.app.provider/table1
这个uri可以用图表示为
知道了这些,当我们想要去查询本地的用户字典里面的数据,我们可以通过ContentResolver.query()获取
Cursor cursor = getContentResolver().query(
uri,
projection,
selection,
selectionArgs,
sortOrder);
下表对这些参数做了一个比较详细的解释
query()参数 | 对应的SQL部分 | 描述 |
---|---|---|
projection | from table_name | 指定查询应用中的某一个表名 |
selection | select column1, column2, column3 | 指定查询的列名 |
selectionArgs | - | 为where中的占位符参数提供的具体值 |
sortOrder | order by column1, column2 | 指定查询结果的排序 |
该查询的结果是Cursor游标,我们可以通过它获取我们需要的数据。
我们也可以通过update,delete,insert实现对数据的更新操作。
例子先不给出了,程序太长了,最后附上源码。
创建自己的ContentProvider
当我们知道了如何通过ContentResolver读取应用信息时,我们也可以自己创建一个ContentPovider供给其它应用访问,其它应用获取到自己的uri,然后通过ContentResolver就可以实现对自己应用数据的动态更新。看下具体实现步骤
1)新建类继承ContentProvider,实现其回调方法
public class MyContentProvider extends ContentProvider{
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@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;
}
}
简单介绍下其方法作用:
onCreate 初始化内容提供者,通常会在这里完成对数据库的创建和升级,返回true表示ContentProvider创建成功,返回false创建失败。注意,只有当ContentResolver访问时,ContentProvider才会被初始化。
query 从内容提供者中查询数据
getType 根据传入的URI来返回MIME类型
- MIME: 一个内容所对应的MIME字符串主要由三部分组成:
1. 必须以vnd开头
2 . 如果内容URI以路径结尾,则后接android.cursor.dir/,如果内容URI以id结尾,则后接android.cursor.item
3 . 最后接上vnd.< authority >,< path >
insert 向ContentProvider中添加一条数据
delete 从内容提供者中删除数据
update更新内容提供中的数据
2)暴露对外访问的uri接口
uri格式一般有两种,一,以路径结尾就表示期望访问该表中的所有数据,二,以id结尾就表示访问该表中拥有相应id的数据。我们可以使用通配符的方式来匹配内容uri
1 . *: 表示匹配任意长度的任意字符
2 .#: 表示匹配任意长度的数字
然后借用UriMather这个类就可以实现匹配内容uri的功能。UriMather中提供了一个addURI()方法,这个方法提供了三个参数,可以分别把权限,路径和一个定义的代码传进去,当匹配成功就会返回传入的第三个自定义代码,然后我们就可以通过返回值知道到底访问的是什么数据。
典型的使用过程如下
//Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。
//UriMatcher类用于匹配Uri,首先把你需要匹配Uri路径全部给注册上
private static final UriMatcher sUriMatcher;
private static final int INCOMING_USER_COLLECTION_URI_INDICATOR = 1;
private static final int INCOMING_SINGLE_USER_URI_INDICATOR = 2;
static {
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.smart.personprovider/person路径,返回匹配码为1
sUriMatcher.addURI(PersonProviderMetaData.AUTHORITY, "person" , INCOMING_USER_COLLECTION_URI_INDICATOR);
//如果match()方法匹配content://com.smart.personprovider/person/1路径,返回匹配码为2
sUriMatcher.addURI(PersonProviderMetaData.AUTHORITY, "person/#",INCOMING_SINGLE_USER_URI_INDICATOR);
}
public static final String AUTHORITY = "com.smart.personprovider";
这就是一个完整的内容提供器就创建完成了,现在任何程序只要拥有该程序的uri就可以访问里面的数据。
流程图
一般我们进程通信也可以使用contentprovider实现
完整实现
可以下载源码自己实现看下,这个例子的注释还是很详细的