1.概要
内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。内容提供器可以选择只对哪一部分数据进行共享。ContentProvider有存储数据的功能,保存的数据只能够被应用程序使用
2.访问其他程序中的数据
内容提供器的用法一般有两种,一种是使用现有的内容提供器来读取和操作相应程序中的数据,另一种是创建自己的内容提供器给我们程序的数据提供外部访问接口。如果一个应用程序通过内容提供器对其数据提供了外部访问接口,那么任何其他的应用程序就都可以对这部分数据进行访问。
2.1 现有的内容提供器
2.1.1 内容URI
对于每一个应用程序来说, 如果想要访问内容提供器中共享的数据,就一定要借助ContentResolver
类,可以通过Context中的getContentResolver()
方法获取到该类的实例。ContentResolver
中提供了一系列的方法用于对数据进行CRUD操作,其中insert() 方法用于添加数据,update() 方法用于更新数据,delete() 方法用于删除数据,query() 方法用于查询数据。ContentResolver
中的增删改查方法都是不接收表名参数的,而是使用一个Uri 参数代替,这个参数被称为内容URI。
内容URI给内容提供器中的数据建立了唯一标识符, 它主要由三部分组成:协议、authority和path。
authority是用于对不同的应用程序做区分的, 一般为了避免冲突, 都会采用程序包名的方式来进行命名。 比如某个程序的包名是com.example.app
, 那么该程序对应的authority就可以命名为com.example.app.provider
。
path则是用于对同一应用程序中不同的表做区分的, 通常都会添加到authority的后面。比如某个程序的数据库里存在两张表: table1和table2, 这时就可以将path分别命名为/table1和/table2,然后把authority和path进行组合,内容URI就变成了com.example.app.provider/table1
和com.example.app.provider/table2
content://com.example.app.provider/table2
获取到URI字符串之后,还要继续解析成uri对象才能作为参数使用
Uri uri = Uri.parse("content://com.example.app.provider/table1")
2.1.2 ContentResolver
访问ContentProvider中共享的数据的方法是:
第一步:通过Context 中的getContentResolver()方法实例化一个ContentResolve对象。
第二步:调用该对象的增删改查方法去操作ContentProvider中的数据。返回的是一个Cursor对象
Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
添加操作:
ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
getContentResolver().insert(uri, values);
更新操作:
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text","1"});
删除操作:
getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });
第三步:可以将数据从Cursor 对象中逐个读取出来了。 读取的思路仍然是通过移动游标的位置来遍历Cursor 的所有行, 然后再取出每一行中相应列的数据
if (cursor != null) {
while (cursor.moveToNext()) {
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
2.1.3 案例实现:
2.2 创建自己的Provider
2.2.1 自定义内容提供器
继承ContentProvider,需要重写全部六个方法
-
onCreate()
初始化内容提供器的时候调用。 通常会在这里完成对数据库的创建和升级等操作, 返回 true 表示内容提供器初始化成功, 返回false则表示失败。 -
query()
从内容提供器中查询数据。 使用uri 参数来确定查询哪张表, projection 参数用于确定查询哪些列, selection和selectionArgs 参数用于约束查询哪些行, sortOrder 参数用于对结果进行排序, 查询的结果存放在Cursor对象中返回。 -
insert()
向内容提供器中添加一条数据。使用uri 参数来确定要添加到的表, 待添加的数据保存在values 参数中。 添加完成后,返回一个用于表示这条新记录的URI。 -
update()
更新内容提供器中已有的数据。使用uri 参数来确定更新哪一张表中的数据,新数据保存在values 参数中, selection和selectionArgs 参数用于约束更新哪些行, 受影响的行数将作为返回值返回。 -
delete()
从内容提供器中删除数据。使用uri 参数来确定删除哪一张表中的数据, selection 和 selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。 -
getType()
根据传入的内容URI来返回相应的MIME类型。
一个能够匹配任意表的内容URI格式就可以写成
content://com.example.app.provider/*
一个能够匹配table1表中任意一行数据的内容URI格式就可以写成
content://com.example.app.provider/table1/#
2.2.2 UriMatcher类
UriMatcher类有匹配内容URI的功能,在这里常用它的两个方法:一个是addURI()方法用来传入URI,它接收三个参数(authority,path,一个自定义代码);另一个是match()方法用来匹配URI,接受一个Uri对象,返回值是某个能够匹配这个Uri对象所对应的自定义代码,利用这个自定义代码,就可以判断出调用方期望访问的是哪张表中的数据了。
2.2.3 getType重写
它是所有的内容提供器都必须提供的一个方法,一个内容URI所对应的MIME字符串主要由3部分组成
- 必须以vnd 开头
- 如果内容URI以路径结尾, 则后接android.cursor.dir/ , 如果内容URI以id结尾, 则后接android.cursor.item/ 。
- 最后接上vnd.authority.path 。
对于content://com.example.app.provider/table1
这个内容URI, 它所对应的MIME类型就可以写成:vnd.android.cursor.dir/vnd.com.example.app.provider.table1
2.2.4 基本步骤
- 新建一个MyContentProvider类去继承ContentProvider
- 重写类中的六个方法,重点注意getType
- 实现UriMatcher方法,方便增删改查中相关匹配URI的操作
- 注册表为内容提供器进行注册,并注明属性
< application > 标签内出现了一个新的标签< provider > , 我们使用它来对新的Provider这个内容提供器进行注册。 android:name 属性指定了内容提供器的类名, android:authorities 属性指定了authority, 而enabled 和exported 属性表示允许DatabaseProvider被其他应用程序进行访问。
<provider
android:name=".DatabaseProvider"
android:authorities="com.example.databasetest.provider"
android:enabled="true"
android:exported="true">
</provider>
需要注意的是获取id的时候调用了Uri 对象的getPathSegments() 方法, 它会将内容URI权限之后的部分以“/”符号进行分割, 并把分割后的结果放入到一个字符串列表中, 那这个列表的第0个位置存放的就是路径, 第1个位置存放的就是id了。 得到了id之后, 再通过selection 和selectionArgs 参数进行约束, 就实现了查询单条数据的功能