1、ContentProvider使用场景
我们知道,一个软件系统的架构通常是这样的:
为了降低上层业务对底层数据的依赖,需要增加一个数据访问层来解耦,我们今天要说的ContentProvider充当的就是数据访问层的角色。ContentProvider提供了一些通用的接口来实现对底层数据(其实是数据库中的表结构数据)进行操作。
2、什么是ContentProvider
内容提供者,是 Android 四大组件之一,是Android提供给上层的一个组件,主要用于实现数据访问的统一管理和数据共享。这里的数据管理是通过定义统一的访问接口来完成,如增删改查。同时,它采用了类似Internet的URL机制,将数据以URI的形式来标识,这样其他App就可以采用一套标准的URI规范来访问同一处数据,而不用关心具体的实现细节。我们知道在Android系统中可能会涉及到一个App的数据被其他App使用的情况,比如通讯录,日历,短信等,这时就需要一套能实现数据共享的机制,这里的ContentProvider就可以提供该功能,其底层使用了binder来完成App进程之间的通信,同时使用匿名共享内存来作为共享数据的载体。当然为了保证数据访问的安全性,ContentProvider还对每处的数据URI增加了权限管理机制,以控制该数据的访问者及访问方式。
3、ContentProvider的作用
进程间 进行数据交互共享,即跨进程通信,其实就是APP与APP分享数据互相获取数据。
4、ContentProvider的使用
1.统一资源表示符URI
Uniform Resource Identifier,即统一资源标识符。
外界进程通过 URI 找到对应的ContentProvider & 其中的数据,再进行数据操作
URI就相当区一个URL接口,我么可以通过它获取到其他APP提供对外开放的一些数据,并对其操作
Uri的结构由以下几个部分组成scheme、authority、path、query和fragment组成 其中authority又分为host和port
它的格式根据划分的详细程度可以分为三种
[scheme:][scheme-specific-part][#fragment]
[scheme:][//authority][path][?query][#fragment]
[scheme:][//host:port][path][?query][#fragment]——最详细的划分形式
http://www.baidu.com:8080/yourpath/fileName.html?id=15&name=du#dmk
[scheme:][//host:port][path][?query][#fragment]
scheme:根据标准格式可以看出这里的scheme就是Uri前面//前面的部分这里也就是http:。
fragment:dmk这个也是比较容易找到的,在#后面
query:id=15&name=du#dmk。从标准格式可以看到在"#"之前"?"之后的部分是query,在这里当然就是id=15&name=du#dmk了。
authority:从格式二中可以看到authority是在//后的部分,它的终点就是在path之前所以这里的authority就是www.baidu.com:8080
path:path就是?之前,主机之后的部分那就是yourpath/fileName.html
host和port:因为主机可以分为host和port所以这里的host和port分别为:www.baidu.com和8080
在Uri中并不是上述所有的字段都必须有的除了scheme、authority是必须要有的,其它的几个path、query、fragment,它们每一个可以选择性的要或不要,但顺序不能变,比方说在上述Uri中没有path那它的格式就为:http://www.baidu.com:8080/?id=15&name=du#dmk。
URI可以分成两种,一种是系统自己提供的,一种是我么自己写的;
系统提供:
ContactsContract.Contacts.CONTENT_URI ————管理联系人
这个一般用到的列有
ContactsContract.Contacts._ID; ——– 获取联系人ID
ContactsContract.Contacts.DISPLAY_NAME; ——– 获取联系人名字
ContactsContract.CommonDataKinds.Phone.CONTENT_URI ————管理联系人的电话
这个一般用到的列有
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME ——– 获取联系人名字
ContactsContract.CommonDataKinds.Phone.NUMBER ——– 获取联系人电话
ContactsContract.CommonDataKinds.Email.CONTENT_URI ————管理联系人的Email
这个一般用到的列有
ContactsContract.CommonDataKinds.Email.DATA——– 获取联系人Email
自己写的:
比如:Uri uri =Uri.parse("content://com.ljq.provider.personprovider/person")
ContentProvider的scheme已经又Android规定好了,scheme就是contnet(就相当于咱用的接口开头是http一样)
2.Android给我么提供了两个对URI进行操作的辅助类 UriMatcher和ContentUris
UriMatcher
作用:用来匹配传入的URI是否与自己需要的数据库表的URI匹配
使用方法:public UriMatcher(int code);
UriMatcher的构造方法 不匹配仁和路径的时候返回的就是code
public void addURI(String authority, String path, int code)
第一个参数其实就是在xml注册时的authorities;
第二个参数要查找的数据库表名
第三个参数就是当匹配到时返回的值
matcher.match(uri)
就是判断是否匹配
ContentUris
作用:用来操作URI路径后面的ID部分,有两个使用方法 ContentUris是一个工具类不需要New出来
就有两个方法,两方法正好想对
使用方法:
第一个方法:
Uri uri = Uri.parse("content://com.example.testcp.FirstContentProvider/users")
Uri insertedUserUri = ContentUris.withAppendedId(uri,1);
用于为路径加上ID部分
第二个方法:
Uri uri = Uri.parse("content://com.example.testcp.FirstContentProvider/users/1");
ContentUris.parseId(uri);
获取Uri中的Id部分
3.MIME数据类型
作用:指定某个扩展名的文件用某种应用程序来打开。(就像你在电脑上用Not++打开txt文件一样)
具体使用:ContentProvider.geType(uri)
ContentProvider根据 URI 返回MIME类型
MIME类型是 一个 包含2部分的字符串
text / html 类型 = text、子类型 = html
MIME类型有2种形式:
// 形式1:单条记录
vnd.android.cursor.item/自定义
// 形式2:多条记录(集合)
vnd.android.cursor.dir/自定义
5、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 boolean onCreate()
// ContentProvider创建后 或 打开系统后其它进程第一次访问该ContentProvider时 由系统进行调用
// 注:运行在ContentProvider进程的主线程,故不能做耗时操作
public String getType(Uri uri)
// 得到数据类型
ContentProvider类并不会直接与外部进程交互,而是通过ContentResolver 类
6、ContentResolver类
即通过 URI 即可操作 不同的ContentProvider 中的数据
外部进程通过 ContentResolver类 从而与ContentProvider类进行交互
为什么要使用通过ContentResolver类从而与ContentProvider类进行交互,而不直接访问ContentProvider类?
答:一般来说,一款应用要使用多个ContentProvider,若需要了解每个ContentProvider的不同实现从而再完成数据交互,操作成本高 & 难度大,所以再ContentProvider类上加多了一个 ContentResolver类对所有的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)
7、系统提供的数据共享
ContactsContract.Contacts.CONTENT_URI ————管理联系人
这个一般用到的列有
ContactsContract.Contacts._ID; ——– 获取联系人ID
ContactsContract.Contacts.DISPLAY_NAME; ——– 获取联系人名字
ContactsContract.CommonDataKinds.Phone.CONTENT_URI ————管理联系人的电话
这个一般用到的列有
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME ——– 获取联系人名字
ContactsContract.CommonDataKinds.Phone.NUMBER ——– 获取联系人电话
ContactsContract.CommonDataKinds.Email.CONTENT_URI ————管理联系人的Email
这个一般用到的列有
ContactsContract.CommonDataKinds.Email.DATA——– 获取联系人Email
多媒体
音频
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI ——- 存储在外部存储器(SD卡)上的文件内容
MediaStore.Audio.Media.INTERNAL_CONTENT_URI ——- 存储在内部存储器上的音频文件的内容
视频
MediaStore.Video.Media.EXTERNAL_CONTENT_URI ——- 存储在外部存储器(SD卡)上的视频文件的内容
MediaStore.Video.Media.INTERNAL_CONTENT_URI ——- 存储在内部存储器上的视频文件的内容
图片
MediaStore.Images.Media.EXTERNAL_CONTENT_URI ——- 存储在外部存储器(SD卡)上的图片文件的内容
MediaStore.Images.Media.INTERNAL_CONTENT_URI ——- 存储在内部存储器上的图片文件的内容
图片一般用到的列有
MediaStore.Images.Media.DISPLAY_NAME ——– 获取图片文件名
MediaStore.Images.Media.DESCRIPTION——– 获取图片详细描述
MediaStore.Images.Media.DATA——– 获取图片保存路径
MediaStore.Images.Media._ID——– 获取图片id
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
最后再说下为何ContentProvider在我们平时开发App中使用的不多?其实,虽然ContentProvider具有数据管理和数据共享的功能,但是多半的优势还是集中在数据共享上,在数据共享的前提下,这个数据管理的优势也才能更加明显。像一些系统内置应用,联系人,日历和短信,就比较适合使用ContentProvider,因为他们经常需要给其他App提供数据。而对于像普通App,一般出于安全原因,不会把数据提供给第三方App使用,而在App内部访问数据库的话,完全可以自己实现一套数据库访问框架,因为ContentProvider为了屏蔽数据库的访问细节,实质上是在其基础上再封装了一层接口而已,而对于App内部的数据库访问来说,没有这个必要,而且ContentProvider作为一个组件与系统依赖性较强,相比自己实现一套数据库管理框架,扩展性和灵活性肯定没那么好。当然也有很多第三方开源的数据库框架可用,比如greenDao,ormlite,realm等。