第九章 使用ContentProvider实现数据共享
理解ContentProvider的功能和意义
ContentProvider类的作用和常用方法
Uri对ContentProvider的作用
理解COntentProvider和ContentResolver的关系
实现自己的ContentProvider
配置ContentProvider
使用ContentProvider操作数据
使用系统ContentProvider提供的数据
监听ContentProvider数据改变
ContentObserver类的作用和常用方法
监听系统的ContentProvider的数据改变
1、数据共享标准:ContentProvider
ContentProvider简介
ContentProvider是不同应用之间进行数据交换的标准API,ContentProvider以某种Uri的形式对外提供数,允许其他应用访问或修改数据;其他应用程序使用ContentResolver根据Uri去访问操作指定数据。
开发ContentProvider步骤:
定义自己的ContentProvider类,该类需要继承Android提供的ContentProvider基类
在Manifest.xml文件中注册这个ContentProvider,注册时需要绑定一个Uri。
<provider
android:authorities="org.crazy.providers.dictprovider"
android:name=".DictProvider"
android:exported="true"
/>
android:authorities和name属性都是必须有的。name属性指定了ContentProvider的类,authorities属性就是制定了Uri,exported属性表示该ContentProvider能否被其他应用访问
通过Manifest.xml中的配置,其他应用就可通过该Uri访问DictProveder所暴露的数据了。
DictProvider如何暴露数据:
除了基础ContentProvider外,还需要重写一些方法,实现CRUD
public boolean onCreate():
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):
public getType(Uri uri):
Uri简介
互联网的URL:
http://www.crazyit.org:80/ethos.php
http://:URL的协议部分,只要通过HTTP协议来访问网站,这个部分就是固定的
www.carzyit.org:域名部分,只要访问指定的网站,这个部分就是固定的
ethos.pht:网站资源部分,当访问者需要访问不同资源时,这个部分是动态改变的
ContentProvider要求的Uri于此类似:
content://org.crazyit.provides.dictprovider/words
content://:这个部分是Android的ContentProvider规定的,暴露ContentProvider、访问ContentProvider的协议默认是content://。
org.crazyit.providers.dictproviders:这个部分是ContentProvider在Manifest.xml中定义的authorities。系统通过这个部分找到操作哪个ContentProvider。只要访问指定的ContentProvider,这个部分就是固定的
words:资源部分(或者说数据部分)。当访问者需要访问不同资源时,这个部分是动态改变的。
为了将一个字符串转换成Uri,Uri工具类提供了parse()方法。
Uri uri = Uri.parse("content://org.crazyit.providers.dictprovider/word/2");
使用ContentResolver操作数据
ContentProvider相当于一个”网站“,它的作用是暴露可供操作的数据;其他程序则通过ContentResolver来操作ContentProvider所暴露的数据,ContentResolver相当于HttpClient。
Context通过getContentResolver();获取该应用默认的ContentResolver。
一旦程序获得了ContentResolver对象,就可以调用它的方法操作数据了:
insert(Uri uri,ContentValues values):向Uri对应的ContentProvider中插入values对应的数据(ContentValues相当于Map);
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处理。
2、开发ContentProvider
ContentProvider和ContentResolver的关系
ContentProvider总是需要和ContentResolver结合使用,ContentProvider负责暴露数据,ContentResolver负责其他应用操作暴露数据。
调用ContentResolver的CRUD方法其实是间接的调用了向外暴露数据的应用的ContentProvider的CRUD的方法。
创建ContentProvider的说明
ContentProvider只要一个生命周期方法,当其他应用通过ContentResolver第一次访问该ContentProvider时,onCreate()方法将会被回调,onCreate()只会被回调一次;
实际开发中,为了确定该ContentProvider实际能处理的Uri,以及确定每个方法中Uri参数所操作的数据,Android系统提供了UriMatcher工具类。
UriMatcher工具类主要提供了两个方法:
void addURI(String anthority,String path,int code):该方法用于向UriMatcher对象注册Uri,其中authority和path组合成一个Uri,而code代表该Uri对应的标识码。
int match(Uri uri):根据前面注册的Uri来判断指定Uri对应的标识码。如果找不到匹配的标识码,该方法返回-1。
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(“org.crazyit.providers.dictprovider”,”word”,1);
matcher.addURI(“org.crazyit.providers.dictproveder”,”word/#”,2);
上面创建的UriMatcher对象注册了两个Uri,意味着以下的匹配结果:
matcher.match(Uri.parse(“content://org.crazyit.providers.dictprovider/words”));//返回标识码1
matcher.match(Uri.parse(“content://org.crazyit.providers.dictprovider/word/2”));//返回标识码2
matcher.match(Uri.parse(“content://org.crazyit.providers.dictprovider/word/10”));//返回标识码2
除此之外,Android还提供了一个ContentUris工具类,用于操作Uri字符串:
withAppendedId(Uri uri,int id):用于为路径加上ID部分。
Uri uri = Uri.parse(“content://org.crazyit.providers.dictprovider/word”);
Uri resultUri = ContentUris.withAppendedId(uri,2);
//生成后的Uri为:”content://org.crazyit.providers.dictprovider/word/2”
parseId(Uri uri):从指定Uri中解析出所包含的ID值。
Uri uri = Uri.parse(“content://org.crazyit.providers.dictprovider/word/2”);
int wordId = ContentUris.parseId(uri);//获取结果为2
对于增删改操作,对数据修改成功后,需要通知修改成功:getContext().getContentResolver().notifyChange(uri,null);
3、操作系统的ContentProvider
使用ContentProvider管理联系人
Android系统提供了Contacts应用程序来管理联系人,而且Android还为联系人管理提供了ContentProvider,这就允许其他应用以ContentResolver来管理联系人数据。
Android系统用于管理联系人的ContentProvider的几个Uri:
ContactsContract.Contacts.CONTENT_URI:管理联系人的Uri
ContactsContract.CommonDataKinds.Phone.CONTENT_URI:管理联系人电话的Uri
ContactsContract.CommonDataKinds.Email.CONTENT_URI:管理联系人的E_mail的Uri
使用ContentProvider管理多媒体内容
Android提供了Camera程序来支持拍照、拍摄视频,用户拍摄的照片、视频都存放在固定的位置。有些时候,其他应用程序需要直接访问Camera所拍摄的照片、视频等,为了这些需求,Android同样为这些多媒体内容提供了ContentProvider。
Android为多媒体提供的ContentProvider的Uri:
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI:存储在外部存储器(SD卡)上的音频文件内容的ContentProvider的Uri。
MediaStore.Audio.Media.INTERNAL_CONTENT_URI:存储在手机内部存储器上的音频文件内容的ContentProvider的Uri。
MediaStore.Image.Media.EXTERNAL_CONTENT_URI:存储在外部存储器(SD卡)上的图片文件内容的ContentProvider的Uri。
MediaStore.Image.Media.INTERNAL_CONTENT_URI:存储在手机内部存储器上的图片文件内容的ContentProvider的Uri。
MediaStore.Video.Media.EXTERNAL_CONTENT_URI:存储在外部存储器(SD卡)上的视频文件内容的ContentProvider的Uri。
MediaStore.Video.Media.INTERNAL_CONTENT_URI:存储在手机内部存储器上的视频文件内容的ContentProvider的Uri。
如果开发图片浏览器,并不需要去遍历SD卡上的图片文件,直接查询系统多媒体ContentProvider即可获取系统中所有的图片信息。(????????)
4、监听ContentProvider的数据变化
ContentObserver
在开发ContentProvider时,不管实现增删改中的哪一个,只要方法导致数据发生变化,程序就调用:
getContext().getContentResolver().notifyChange(uri,null);
这行代码用于通知所有注册在该Uri上的监听者:该ContentProvider所共享的数据发生了变化
为了监听指定ContentProvider的数据变化,需要通过ContentResolver向指定Uri注册ContentObserver监听器。ContentResolver提供了如下方法注册监听器:
registerContentObserver(Uri uri,boolean notifyForDescendents,ContentObserver observer);
uri:该监听器所监听的ContentProvider的Uri。
notifyForDescendents:如果该参数设置为true,加入注册监听的Uri为content://abc,那么如果Uri为content://abc/xyz/foo的数据改变时也会出发该监听器;如果参数设为false,加入注册监听的Uri为content://abc,那么只有content://abc的数据发生改变才会触发该监听器。
observer:监听器实例
getContentResolver().registerContentObserver(Uri.parse(“content://sms”),true,new SmsObserver(new Handler()));
这里的SMSObserver就是ContentObserver的子类(默认构造需要传入一个Handler对象)