我正在参加「掘金·启航计划」
前言
做过Android开发的应该知道 ContentProvider
是 Android 的四大组件之一,其在 Android 中的作用是不小的。
平常开发中主要用来实现应用程序之间的(跨应用)数据共享,例如手机中联系人APP就使用了ContentProvider
读取和修改联系人的数据,但需要先获得相应权限。
认识 ContentProvider
ContentProvider 可理解为 Android 应用对外开放的interface,对于符合其所定义的 URI 格式请求,均可以正常执行操作。第三方应用可使用 ContentResolver 对象通过与 ContentProvider 同名方法请求执行,被执行的就是 ContentProvider 中同名方法。
因此 ContentProvider 中对外可以访问方法,在 ContentResolver 中均有同名方法,是一一对应的
ContentProvider 如何实现数据共享?
统一资源标识符(URI)
URI 代表待操作数据,可用来标识 ContentProvider,这样就可以通过指定的 URI 找到对应 ContentProvider,来获取或修改数据。
在 Android 中 URI 的格式如下:
URI =
<schema>://<authority>/<path>/<id>
如:content://com.demo.provider/User/1
- schema
表示是Android 内容 URI,说明由 ContentProvider 控制数据,是固定形式,不可更改
- authority
URI 的授权部分,是唯一标识符,用来定位 ContentProvider。格式一般是自定义 ContentProvider 类的完全名称,注册时需要用到。如:com.demo.provider.MyProvider
- path
路径,一般用数据表的名字,指向数据库中的某个表
- id
指向特定的记录,如数据表中某个记录(若无指定,则返回全部记录)
MIME 数据类型
MIME 是指定某个扩展名的文件用一种应用程序来打开,就像你用浏览器查看 PDF 格式的文件,浏览器会选择合适的应用来打开一样。
Android 中的工作方式跟 HTTP 类似,ContentProvider 会根据 URI 来返回 MIME 类型, ContentProvider 会返回一个包含两部分的字符串。
MIME 组成 = 类型 + 子类型。
text/html application/pdf
...
ContentProvider 根据 URI 返回 MIME 类型
ContentProvider.geType(uri) ;
Android 遵循类似的约定来定义 MIME 类型,每个内容类型的 Android MIME 类型有两种形式:多条记录(集合)和单条记录。
- 多条记录:
vnd.android.cursor.dir/<custom>
- 单条记录:
vnd.android.cursor.item/<custom>
vnd 表示这些类型和子类型具有非标准的、供应商特定的形式。Android 中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型 vnd. 之后的内容可以按照格式随便填写。
在使用 Intent 时,会用到 MIME,根据 MIME Type 打开符合条件的 Activity。
<activity android:name=".TestActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/jeanboy.first" />
</intent-filter>
</activity>
创建自定义 ContentProvider
创建类 MyContentProvider,继承 ContentProvider 并实现相关方法
public class MyContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
// TODO 初始化操作
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO 查询
return null;
}
@Override
public String getType(Uri uri) {
// TODO MIME Type
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO 插入数据
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO 删除数据
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO 更新数据
return 0;
}
}
记得要在
AndroidManifest.xml
中注册(静态注册)
<provider
android:name=".ui.provider.MyProvider"
android:authorities="com.demo.myprovider" />
使用自定义 ContentProvider
在第三方应用中,要如何利用 URI 来执行共享数据数的操作?就是使用 ContentResolver 来完成。
获取 ContentResolver 实例:
ContentResolver resolver = getContentResolver();
ContentResolver 有几个数据库操作:查询、插入、更新、删除
public final Cursor query (Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
public final Uri insert (Uri url, ContentValues values)
public final int update (Uri uri, ContentValues values, String where,
String[] selectionArgs)
public final int delete (Uri url, String where, String[] selectionArgs)
熟悉ContentProvider权限
在 AndroidManifest.xml 配置信息中 provider 标签有三个额外参数 permission、readPermission、writePermission。
代码示例:
<provider
android:name=".ui.provider.MyProvider"
android:authorities="com.demo.myprovider"
android:exported="true"
android:readPermission="com.demo.provider.permission.read"
android:writePermission="com.demo.provider.permission.write"
android:permission="com.demo.provider.permission"/>
示例代码中有几个参数要注意一下:
- exported
用于指示是否能被其他程序应用组件调用或跟他交互; 取值为(true | false)
设置成true,则能够被调用或交互,否则不能;设置为 false 时,只有同一应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该任务。
- readPermission
使用 ContentProvider 查询功能的权限,即使用 query()
函数的权限
- writePermission
使用 ContentProvider 的修改功能的权限,即使用 insert()
、update()
、delete()
函数的权限
- permission
APP读、写 ContentProvider 中数据所必需的权限名称
若只设置 permission 权限,那么拥有这个权限的应用就可以实现对 ContentProvider 进行读写; 若同时设置了 permission 和 readPermission 则具有 readPermission 权限的应用才可以读,拥有 permission 权限的才能写。换句话说只拥有 permission 权限是不能读的,因为 readPermission 优先级要高于 permission; 若同时设置 readPermission、writePermission、permission 那么 permission 就无效