Android四大组件之ContentProvider
Android四大组件之ContentProvider
为了在应用程序之间交换数据,Android提供了ContentProvider,它是不同应用程序之间进行数据交换的标准API,当一个应用程序需要把自己的数据暴露给其他程序使用时,该应用程序就可通过提供ContentProvider来实现;其他应用程序就可通过ContentResolver来操作ContentResolver暴露的数据。
ContentProvider简介
ContentProvider是不同应用程序之间进行数据交换的标准API,ContentProvider以某种Uri的形式对外提供数据,允许其他应用访问或修改数据;其他应用程序使用COntentResolver根据Uri去访问操作指定数据。
一旦某个应用程序通过COntentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可通过该接口来操作该应用程序的内部数据,包括增加数据、删除数据、修改数据、查询数据等。
ContentResolver只是提供数据的访问接口,并不是像网站一样对外提供完整的页面。开发一个ContentProvider的步骤如下所示:
- 定义自己的ContentProvider类,该类需要继承Android提供的ContentProvider基类。
- 向Android系统注册ContentProvider,即在AndroidManifest.xml文件中注册这个ContentProvider,就想注册Activity一样。注册ContentProvider时需要为它绑定一个Uri。
除此之外,自己定义的ContentProvider类还需要提供如下几个方法:
- public boolean onCreate():该方法在ContentProvider创建后会被调用,当其他应用程序第一次访问ContentProvider时,该ContentProvider会被创建出来,并立即回调该onCreate()方法。
- public Uri insert(Uri uri ,ContentValues values):根据该Uri插入values对应的数据。
- public int delete(Uri uri ,String selection ,String[] selectionArgs):根据Uri删除selection条件所匹配的全部记录。
- public int update(Uri uri ,ContentValues values ,String selection ,String[] selectionArgs):根据Uri修改selection条件所匹配的全部记录。
- public Cursor query(Uri uri ,String[] projection ,String selectionn ,String[] selectionArgs ,String sortOrder):根据Uri查询出selection条件所匹配的全部记录,其中projection就是一个列名列表,表明只选择出指定的数据列。
- public String getType(Uri uri):该方法用于返回当前Uri所代表的数据的MIME类型。如果该Uri对应的数据可能包括多条记录,那么MIME类型字符串应该以vnd.android.cursor.dir/开头;如果该Uri对应的数据只包含一条记录,那么MIME类型字符串应该以vnd.android.cursor.item/开头。
Uri简介
ContentProvider要求的Uri与互联网的URL类似,例如如下Uri:
content://org.crazyit.providers.dictprovider/words
它可以分为如下三部分:
- content://:这个部分是Android的ContentProvider规定的,就像是上网的协议默认是http://一样。暴露ContentProvider、访问ContentProvider的协议默认是content://。
- org.crazyit.providers.dictprovider:这个部分就是COntentProvider的authorities。系统就是由这个部分来找到操作哪个ContentProvider的。只要访问指定的ContentProvider,这个部分就是固定的,
- words:资源部分(或者说数据部分)。当访问者需要访问不同资源时,这个部分是动态改变的。
为了将一个字符串转换成Uri,Uri工具类提供了parse()静态方法。例如,如下代码即可将 字符串转换为Uri:
Uri uri = Uri.parse("content://org.crazyit.providers.dictprovider/word/2")
ContentResolver操作数据
ContentProvider的作用是暴露可供操作的数据;其他应用程序则通过ContentProvider来操作ContentProvider所暴露的数据,ContentResolver相当于HttpClient。
Context提供了如下方法来获取ContentResolver对象:
- getContentResolver():获取该应用默认的ContentResolver对象。
一旦在程序中获得了ContentResolver对象之后,接下来就可调用ContentResolver的如下方法来操作数据了。
- insert(Uri uri ,ContentValues values):向Uri对应的ContentProvider中插入values对应的数据。
- delete(Uri uri ,String where ,String[] selectionArgs):删除Uri对应的ContentProvider中where提交匹配的数据。
- update(Uri uri ,ContentValues values ,String where ,String[] selectionArgs):更新Uri对应的ContentProvider中where提交匹配的数据。
- query(Uri uri ,String[] projection ,STring selection ,String[] selectionArgs ,String sortOrder):查询Uri对应的ContentProvider中where提交匹配的数据。
一般来说,ContentProvider是单实例模式的,当多个应用程序通过ContentResolver来操作ContentProvider提供的数据时,ContentResolver调用的数据操作将会委托给同一个ContentProvider处理。
ContentProvider与ContentResolver的关系
从ContentResolver、ContentProvider和Uri的关系来看,无论是ContentResolver,还是ContentProvider,它们所提供的增删改查方法的第一个参数都是Uri。也就是说,Uri是ContentProvider和ContentResolver进行数据交换的标识。ContentResolver对指定Uri执行增删改查的数据操作,但Uri并不是真正的数据中心,因此这些增删改查操作会委托给该Uri对应的ContentProvider来实现。
ContentProvider、Uri、ContentResolver三者之间的关系如下图所示:

以指定Uri为标识,ContentResolver可以实现“间接调用”ContentProvider的增删改查方法:
- 当A应用调用ContentResolver的insert()方法时,实际上相当于调用了该Uri对应的ContentProvider的insert()方法。
- 当A应用调用ContentResolver的update()方法时,实际上相当于调用了该Uri对应的ContentProvider的update()方法。
- 当A应用调用ContentResolver的delete()方法时,实际上相当于调用了该Uri对应的ContentProvider的delete()方法。
- 当A应用调用ContentResolver的query()方法时,实际上相当于调用了该Uri对应的ContentProvider的query()方法。
开发ContentProvider子类
开发ContentProvider只要如下两步:
- 开发一个ContentProvider子类,该子类需要实现query()、insert()、update()和delete()等方法。
- 在AndroidManifest.xml文件中注册该ContentProvider,指定android:authorities属性。
配置ContentProvider
只要为<applicaton.../>元素添加了<provider.../>子元素即可配置ContentProvider。例如如下的配置片段:
<provider
android:name=".FirstProvider"
android:authorities="org.crazyit.providers.firstprovider"
android:exported="true"/>
配置ContentProvider时通常指定如下属性:
- name:指定该ContentProvider的实现类的类名。
- authorities:指定该ContentProvider对应的Uri(相当于为该ContentProvider分配一个域名。)
- android:exported:指定该ContentProvider是否允许其他应用调用。如果将该属性设为false,那么该ContentProvider将不允许其他应用调用。
为了确定ContentProvider实际能处理的Uri,以及确定每个方法中Uri参数所操作的数据,Android系统提供了UriMatcher工具类,主要提供了如下两个方法:
- void addURI(String authority ,String path ,int code):该方法用于向UriMatcher对象注册Uri。其中authority和path组合成一个Uri,而code则代表该Uri对应的标识码。
- int match(Uri uri):根据前面注册的Uri来判断指定Uri对应的标识码。如果找不到匹配的标识码,就会返回-1。
Android还提供了一个ContentUris工具类,它是一个操作Uri字符串的工具类,提供了如下两个工具方法:
- withAppendedId(uri , id):用于为路径加上ID部分。
- parseId(uri):用于从指定Uri中解析出所包含的ID值。
操作系统的ContentProvider
Android系统本身提供了大量的ContentProvider,使用ContentResolver操作系统的ContentProvider数据的步骤也是两步:
- 调用Context的getContentResolver()获取ContentResolver对象;
- 根据需要调用ContentResolver的insert()、delete()、update()和query()方法操作数据。
Android系统用于管理联系人的ContentProvider的几个Uri如下:
- ContactsContract.Contacts.CONTENT_URI:管理联系人的Uri。
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI:管理联系人的电话的Uri。
- ContactsContract.CommonDataKinds.Email.CONTENT_URI:管理联系人的E-mail的Uri。
Android为多媒体提供的ContentProvider的Uri如下所示:
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI:存储在外部存储其上的音频文件内容的ContentProvider的Uri。
- MediaStore.Audio.Media.INTERNAL_CONTENT_URI:存储在手机内部存储器上的音频文件内容的ContentProvider的Uri。
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI:存储在外部存储器上的图片文件内容的ContentProvider的Uri。
- MediaStore.Images.Audio.Media.INTERNAL_CONTENT_URI:存储在手机内部存储器上的图片文件内容的ContentProvider的Uri。
- MediaStore.Video.Media.EXTERNAL_CONTENT_URI:存储在外部存储器上的视频文件内容的ContentProvider的Uri。
- MediaStore.Video.Audio.Media.INTERNAL_CONTENT_URI:存储在手机内部存储器上的视频文件内容的ContentProvider的Uri。
监听ContentProvider的数据改变
在之前的介绍中,只要导致了ContentProvider数据发生了改变,程序中就调用如下代码:
getContext().getContentResolver(),notifyChange(uri ,null);
为了在应用程序中监听ContentProvider数据的改变,需要利用Android提供的ContentObserver基类。监听ContentProvider数据改变的监听器需要继承ContentObserver类,并重写该基类所定义的onChange(boolean selfChange)方法--当所监听的ContentProvider数据发生改变时,该onChange()方法将会被触发。
为了监听指定ContentProvider的数据变化,需要通过ContentResolver向指定Uri注册ContentObserver监听器。ContentResolver提供了如下方法来注册监听器:
- registerContentObserver(Uri uri , boolean notifyForDescendents , ContentObserver observer)
这个方法的三个参数分别表示:
- uri:该监听器所监听的ContentProvider的Uri。
- notifyForDescendents:如果该参数设为true,假如注册监听的Uri为content://abc,nameUri为contetn://abc/xyz、content://abc/xyz/foo的数据改变时也会触发该监听器;如果设为false,那么只有content://abc的数据发生改变时才会触发该监听器。
- observer:监听器实例。
提供程序访问的替代形式
提供程序访问的三种替代形式在应用开发的过程中十分重要:
- 批量访问:可以通过ContentProviderOperation类中的方法创建一批访问调用,然后通过ContentResolver.applyBatch()执行它们。
- 异步查询:应该在单独线程中执行查询。
- 通过Intent访问数据:尽管无法直接向提供程序发送Intent,但是可以向提供程序的应用发送Intent,后者通常具有修改提供程序数据的最佳配置。
好的,ContentProvider就介绍这些吧!!欢迎关注我的微信公众号!
