转载请注明出处:www.leoyanblog.com
本文出自 LeoYan 的博客
本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。
ContentProvider 是什么?
ContentProvider(数据提供者)是应用程序之间共享数据的一种接口机制,是一种更为高级的数据共享方法。
ContentProvider 可以指定需要共享的数据,而其他应用程序则可以在不知道数据来源、路径的情况下,对共享数据进行增删改查等操作。
在 Android 系统中,许多 Android 系统内置的数据也是通过 ContentProvider 提供给用户使用,例如通讯录、音视频文件和图像文件等。
URI
URI(统一资源标识符)代表要操作的数据,可以用来标识每个 ContentProvider,这样你就可以通过指定的URI找到想要的 ContentProvider, 从中获取或修改数据。
在 Android 中 URI 的格式如下所示:
1 | content://com.leo.peopleprovider/people/7 |
它可以分为如下三部分:
- content://
这个部分是 Android 的 ContentProvider 规定的,就像是上网的协议默认是 http:// 一样。暴露 ContentProvider、访问 ContentProvider 的协议默认是 content:// 。
- com.leo.peopleprovider
这个部分就是 ContentProvider 的 authorities (主机名)。是唯一标识符,用来定位 ContentProvider。系统就是由这个部分来找到操作哪个 ContentProvider 的。
- /people
资源部分(或者说数据部分)。指向一个对象集合,一般用表的名字。当访问者需要访问不同资源时,这个部分是动态改变的。
- /7
指向特定的记录,这里表示操作 people 表 id 为 7 的记录。如果要操作 people 表中 id 为 7 的记录的 name 字段,这部分应为 /7/name 即可。
其中 /people 部分和 /7 部分:是每个 ContentProvider 内部的路径部分
URI 模式匹配通配符
* 匹配的任意长度的任何有效字符的字符串。
# 匹配的任意长度的数字字符的字符串。
如:
content://com.leo.peopleprovider/* 匹配 provider 的任何内容 url
content://com.leo.peopleprovider/people/# 匹配 people 表中的所有行
MIME
MIME,全称 Multipurpose Internet Mail Extensions,多功能 Internet 邮件扩充服务。MIME 类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。Android 中的工作方式也是类似的,在 ContentProvider 的 getType(Uri) 方法中,可以显示的返回一个 MIME 类型,该方法返回一个字符串,可以是任意的字符串,当我们显示的返回该 MIME 类型的时候,相当于通过该方法的验证,Provider 可以识别自身其他方法返回的 Cursor 的内容,不需要在进行更多的验证;如果返回其他的字符串 (非 android 能够识别的 MIME 类型,例如直接返回当前的包名),则 Provider 在执行其他方法后,返回 Cursor 类型的时候,需要再次进行验证。MIME 类型一般包含两部分,如:
text/html
text/css
text/xml
application/pdf
分为类型和子类型,Android 遵循类似的约定来定义 MIME 类型,每个内容类型的 Android MIME 类型有两种形式:多条记录(集合)和单条记录。
集合记录:
1 | vnd.android.cursor.dir/自定义 |
单条记录:
1 | vnd.android.cursor.item/自定义 |
vnd 表示这些类型和子类型具有非标准的、供应商特定的形式。Android 中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型可以按照格式自己填写。
在使用 Intent 时,会用到 MIME,根据 Mimetype 打开符合条件的活动。
下面分别介绍 Android 系统提供了两个用于操作 Uri 的工具类:ContentUris 和 UriMatcher。
ContentUris
ContetnUris 包含一个便利的函数 withAppendedId() 来向 URI 追加一个 id。
1 | Uri uri = Uri.parse("content://com.leo.peopleprovider/people") |
同时提供 parseId(uri) 方法用于从 URL 中获取 ID:
1 | Uri uri = Uri.parse("content://com.leo.peopleprovider/people/7") |
UriMatcher
UriMatcher 本质上是一个文本过滤器,用在 ContentProvider 中帮助我们过滤,分辨出查询者想要查询哪个数据表。
举例说明:
- 第一步,初始化:
1 | UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); |
- 第二步,注册需要的Uri:
1 | // MULTIPLE_PEOPLE 和 SINGLE_PEOPLE 是两个 int 型数据 |
- 第三部,与已经注册的Uri进行匹配:
1 | /* |
ContentProvider 的主要方法
public boolean onCreate()
ContentProvider 创建后或打开系统后其它应用第一次访问该 ContentProvider 时调用。
public Uri insert(Uri uri, ContentValues values)
外部应用向 ContentProvider 中添加数据。
public int delete(Uri uri, String selection, String[] selectionArgs)
外部应用从 ContentProvider 删除数据。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):
外部应用更新 ContentProvider 中的数据。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
供外部应用从 ContentProvider 中获取数据。
public String getType(Uri uri)
该方法用于返回当前 Url 所代表数据的 MIME 类型。
ContentResolver
ContentResolver 通过 URI 来查询 ContentProvider 中提供的数据。除了 URI 以外,还必须知道需要获取的数据段的名称,以及此数据段的数据类型。如果你需要获取一个特定的记录,你就必须知道当前记录的 ID,也就是 URI 中 /7 那部分。
ContentResolver 类提供了与 ContentProvider 类相同签名的四个方法:
public Uri insert(Uri uri, ContentValues values) // 添加
public int delete(Uri uri, String selection, String[] selectionArgs) // 删除
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) // 更新
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) // 获取
实例代码:
1 | ContentResolver resolver = getContentResolver(); |
ContentObserver
ContentObserver (内容观察者),目的是观察特定 Uri 引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当 ContentObserver 所观察的 Uri 发生变化时,便会触发它.
下面是使用内容观察者监听短信的例子:
1 | public class MainActivity extends Activity { |
同时可以在 ContentProvider 发生数据变化时调用
getContentResolver().notifyChange(uri, null) 来通知注册在此 URI 上的访问者。
1 | public class UserContentProvider extends ContentProvider { |