分享几点Android中 ContentProvider 的使用技巧

1,056 阅读4分钟

我正在参加「掘金·启航计划」

前言

做过Android开发的应该知道 ContentProvider 是 Android 的四大组件之一,其在 Android 中的作用是不小的。

平常开发中主要用来实现应用程序之间的(跨应用)数据共享,例如手机中联系人APP就使用了ContentProvider 读取和修改联系人的数据,但需要先获得相应权限。

认识 ContentProvider

ContentProvider 可理解为 Android 应用对外开放的interface,对于符合其所定义的 URI 格式请求,均可以正常执行操作。第三方应用可使用 ContentResolver 对象通过与 ContentProvider 同名方法请求执行,被执行的就是 ContentProvider 中同名方法。

因此 ContentProvider 中对外可以访问方法,在 ContentResolver 中均有同名方法,是一一对应的

01.jpg

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 就无效