一、ContentProvider 介绍
定义
ContentProvider是Android四大组件之一,其本质上是一个标准化的数据管道,它屏蔽了底层的数据管理和服务等细节,以标准化的方式在Android 应用间共享数据、数据交互,跨进程通信。
原理
ContentProvider的底层是采用 Android中的Binder机制
具体请看文章 图文详解 Android Binder跨进程通信的原理
特点
安全,访问简单,高效,统一了数据的访问方式。
二、使用方法
1、自定义ContentProvider类
该类需要继承Android提供的ContentProvider基类。然后实现ContentProvide的抽象方法,实现对数据的CRUD(create,retrieve,update,delete),还有onCreate和getType。
- onCreate(),该方法在ContentProvider创建后会被调用,当其他应用程序第一次访问ContentProvide时,该ContentProvider会被创建出来,并立即回调该onCreate方法
- getType(Uri uri), 返回 当前Uri所代表的数据的MIME类型。如果该Uri对应数据可能包括多条记录,那么MIME类型字符串应该以 vnd.android.cursor.dir/开头;如果该Uri对应的数据只包含一条记录,那么返回MIME类型字符串应该以vnd.android.cursor.item/开头。
2、在AndroidManifest.xml中进行注册。
注册的时候,与注册广播类似。
- 属性name指定ContentProvide实现类的类名;
- 属性authorities是Uri标识,ContentProvide就是以这个Uri的形式对外提供数据,ContentResolve也是根据该Uri进行访问操作该应用的数据的,可以理解为网站的域名
- 属性exported指定是否对外暴露数据,只有为true时,其他应用才可以访问该应用的数据,缺省值为true。
3、其他APK用ContentResolver对数据进行CRUD操作
通过调用Content的 getContentResolver() 方法获取 ContentResolver对象实例,其实ContentResolver的作用类似于HttpClient,获取对象后就可以根据Uri对应用的数据进行CRUD操作了。
三、其他相关的类
Uri
与网站https://www.oracle.com/index.html的Uri的规则类似
content://com.lzb.provide.myContentProvide:200/students 说明如下:
- content://:协议部分,表示ContentProvider使用的协议,这个是固定的。
- com.lzb.provide.myContentProvide:URI 的标识,也是属性authorities定义的部分,系统就是通过这个部分找到要操作那个ContentProvide,为了保证URI标识的唯一性,它一般是一个完整的、小写的类名(包.小写的类名)。
- :200:端口号,Uri默认的端口是8080。
- students:资源部分(资源所在的路径),访问者可以访问不同的资源,这个是动态的;如果想要访问具体那行数据,可以在后面加上该行的ID,例如上述要该路径下的第1条记录,那么Uri可以写为content://com.lzb.provide.myContentProvide/students/1。
UriMatcher
作用:在ContentProvider 中注册URI,根据 URI 匹配 ContentProvider 中对应的数据表。
该类主要提供了如下两个方法:
- public void addURI(String authority, String path, int code) 用于向UriMatcher注册Uri,其中参数authority与path组成一个Uri,参数code 是该Uri对应的标识码
- public int match(Uri uri) 根据前面注册的Uri返回其对应的标识码,如果在UriMatcher中找不到对应的Uri则返回-1。
// 步骤1:初始化UriMatcher对象
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//常量UriMatcher.NO_MATCH = 不匹配任何路径的返回码
// 即初始化时不匹配任何东西
// 步骤2:在ContentProvider 中注册URI(addURI())
int URI_CODE_a = 1;
int URI_CODE_b = 2;
matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a);
matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b);
// 若URI资源路径 = content://cn.scu.myprovider/user1 ,则返回注册码URI_CODE_a
// 若URI资源路径 = content://cn.scu.myprovider/user2 ,则返回注册码URI_CODE_b
// 步骤3:根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())
@Override
public String getType(Uri uri) {
Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");
switch(matcher.match(uri)){
// 根据URI匹配的返回码是URI_CODE_a
// 即matcher.match(uri) == URI_CODE_a
case URI_CODE_a:
return tableNameUser1;
// 如果根据URI匹配的返回码是URI_CODE_a,则返回ContentProvider中的名为tableNameUser1的表
case URI_CODE_b:
return tableNameUser2;
// 如果根据URI匹配的返回码是URI_CODE_b,则返回ContentProvider中的名为tableNameUser2的表
}
}
ContentUris
作用:操作 URI
核心方法有两个:withAppendedId()、parseId()
// withAppendedId()作用:向URI追加一个id
Uri uri = Uri.parse("content://cn.scu.myprovider/user")
Uri resultUri = ContentUris.withAppendedId(uri, 7);
// 最终生成后的Uri为:content://cn.scu.myprovider/user/7
// parseId()作用:从URL中获取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")
long personid = ContentUris.parseId(uri);
//获取的结果为:7
ContentObserver
作用:观察 Uri引起 ContentProvider 中的数据变化 & 通知外界(即访问该数据访问者)
操作步骤如下:
1、注册ContentObserver 内容观察者 registerContentObserver
2、继承 ContentObserver 实现 onChange方法
3、当该URI的ContentProvider数据发生变化时,通知外界getContext().getContentResolver().notifyChange(uri, null);
4、解除观察者 getContentResolver().unregisterContentObserver(uri)。
四、应用场景
为了降低上层业务对底层数据的依赖,需要增加一个数据访问层来解耦,ContentProvider充当的就是数据访问层的角色。内容提供者可以使用不同的方式来存储数据,数据可以被存放在数据库,文件,甚至是网络。
- 可以使用自定义ContentProvider。
- 可以使用系统自带的ContentProvider,如音频、视频、图片、通讯录和短信。
- ContentResolver可以与ContentProvider在相同的进程间使用,但一般情况是跨进程使用。
参考链接
https://blog.youkuaiyun.com/lu1024188315/article/details/78303467
https://blog.youkuaiyun.com/carson_ho/article/details/76101093
更详细的的解析 https://blog.youkuaiyun.com/luoshengyang/article/details/6946067