Content Provider

来源:互联网 发布:系统网络架构图 编辑:程序博客网 时间:2024/04/28 21:07

ContentProvider 基本概念

      

1、  Content Provider提供为存储和获取数据提供了统一的接口

比如说,电话上面存储了联系人等信息

2、  使用ContentProvider可以在不同的应用程序之间共享数据

在Sqlite中只能在当前的应用程序中访问当前应用程序的数据库,无法访问别的应用程序数据库,ContentProvider可以在不同的应用程序之间共享数据

3、  Android为常见的一些数据提供了ContentProvider(包括音频、视频、图片和通讯录等的)

 

数据模型

 

URI(统一资源标识)

       URI与URL
在Android中广泛应用URI,而不是URL。URL标识资源的物理位置,相当于文件的路径;而URI则是标识资源的逻辑位置,并不提供资源的具体位置。比如说电话薄中的数据,如果用URL来标识的话,可能会是一个很复杂的文件结构,而且一旦文件的存储路径改变,URL也必须得改动。但是若是URI,则可以用诸如content : //contract /people这样容易记录的逻辑地址来标识,而且并不需要关心文件的具体位置,即使文件位置改动也不需要做变化,当然这都是对于用户来说,后台程序中URI到具体位置的映射还是需要程序员来改动的。

       在Android中,ContentProvider是数据对外的接口,程序通过ContentProvider访问数据而不需要关心数据具体的存储及访问过程,这样既提高了数据的访问效率,同时也保护了数据。Activity类中有一个继承自ContentWapper的getContentResolver()无参数方法,该方法返回一个ContentResolver对象,通过调用其query、insert、update、delete方法访问数据。这几个方法的第一个参数均为URI型,用来标识资源。

Android的ContentProvider URI有固定的形式:
content : //contract / people
前缀:固定为content : //
认证:contract 资源的唯一标识符
路径:people 具体的资源类型

1、  每一个Content Provider都拥有一个公共的URI,这个URI用于表示这个Content Provider所提供的数据。

2、  Android所提供的Content Provider都存放在android.provider包中

 

ContentProvider所提供的函数

1、  query():查询

2、  insert():插入

3、  update():更新

4、  delete():删除

5、  getType():得到的数据类型

6、  onCreate():创建时的回调函数

 

实现Content Provider的过程

1、  定义一个CONTENT_URI常量

2、  定义一个类,继承ContentProvider

3、  实现query、insert、update、delete、getType和onCreate方法

4、  在AndroidManifest.xml当中进行声明

Android中的Content provider机制可支持在多个应用中存储和读取数据。这也是跨应用共享数据的唯一方式。在android系统中,没有一个公共的内存区域,供多个应用共享存储数据。

Android提供了一些主要数据类型的Content provider,比如音频、视频、图片和私人通讯录等。可在android.provider包下面找到一些android提供的Contentprovider。可以获得这些Content provider,查询它们包含的数据,当然前提是已获得适当的读取权限。

如果想公开自己的数据,那么可有两种办法:



1.     创建自己的Content provider,需要继承ContentProvider类;

2.        如果你的数据和已存在的Content provider数据结构一致,可以将数据写到已存在的Content provider中,当然前提是获取写该Content provider的权限。比如把OA中的成员通讯信息加入到系统的联系人Content provider中。

Content provider基础所有Content provider都需要实现相同的接口用于查询Content provider并返回数据,也包括增加、修改和删除数据。首先需要获得一个ContentResolver的实例,可通过Activity的成员方法getContentResovler()方法:

1.  ContentResolver cr = getContentResolver();

复制代码


ContentResolver实例带的方法可实现找到指定的Content provider并获取到Content provider的数据。ContentResolver的查询过程开始,Android系统将确定查询所需的具体Content provider,确认它是否启动并运行它。android系统负责初始化所有的Content provider,不需要用户自己去创建。实际上,content provider的用户都不可能直接访问到content provider实例,只能通过ContentResolver在中间代理。数据模型Content provider展示数据类似一个单个数据库表。其中:

·          每行有个带唯一值的数字字段,名为_ID,可用于对表中指定记录的定位;

·          Content provider返回的数据结构,是类似JDBC的ResultSet,在android中,是Cursor对象。

URI每个content provider定义一个唯一的公开的URI,用于指定到它的数据集。一个content provider可以包含多个数据集(可以看作多张表),这样,就需要有多个URI与每个数据集对应。这些URI要以这样的格式开头:

1.  content://

复制代码


表示这个uri指定一个content provider。如果你想创建自己的content provider,最好把自定义的URI设置为类的常量,这样简化别人的调用,并且以后如果更新URI也很容易。android定义了CONTENT_URI常量用于URI,比如:

1.  android.provider.Contacts.Phones.CONTENT_URI 

2.      android.provider.Contacts.Photos.CONTENT_URI

复制代码


要注意的是上面例子中的Contacts,已经在android 2.0及以上版本不赞成使用。查询Content provider要想使用一个content provider,需要以下信息:

·          定义这个content provider的URI

·          返回结果的字段名称

·          这些字段的数据类型

如果需要查询content provider数据集的特定记录(行),还需要知道该记录的ID的值。构建查询查询就是输入URI等参数,其中URI是必须的,其他是可选的,如果系统能找到URI对应的content provider将返回一个Cursor对象。可以通过ContentResolver.query()或者Activity.managedQuery()方法。两者的方法参数完全一样,查询过程和返回值也是相同的。区别是,通过Activity.managedQuery()方法,不但获取到Cursor对象,而且能够管理Cursor对象的生命周期,比如当Activity暂停(pause)的时候,卸载该Cursor对象,当Activity restart的时候重新查询。另外,也可以对一个没有处于Activity管理的Cursor对象做成被Activity管理的,通过调用Activity.startManaginCursor()方法。类似这样:

1.  Cursor cur = managedQuery(myPerson, null, null, null,null);

复制代码


其中第一个参数myPerson是Uri类型实例。如果需要查询的是指定行的记录,需要用_ID值,比如ID值为23,URI将是类似:

1.  content://. . . ./23

复制代码


android提供了方便的方法,让开发者不需要自己拼接上面这样的URI,比如类似:

1.  Uri myPerson =ContentUris.withAppendedId(People.CONTENT_URI, 23);

复制代码


或者:

1.  Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");

复制代码


二者的区别是一个接收整数类型的ID值,一个接收字符串类型。其他几个参数:

·          names,可以为null,表示取数据集的全部列,或者声明一个String数组,数组中存放列名称,比如:People._ID。一般列名都在该Contentprovider中有常量对应;

·          针对返回结果的过滤器,格式类似于SQL中的WHERE子句,区别是不带WHERE关键字,如果返回null表示不过滤,比如name=?

·          前面过滤器的参数,是String数组,是针对前面条件中?占位符的值;

·          排序参数,类似SQL的ORDERBY字句,不过不需要写ORDER BY部分,比如name desc,如果不排序,可输入null。

返回值是Cursor对象,游标位置在第一条记录之前。下面实例适用于android 2.0及以上版本,从android通讯录中得到姓名字段:

1.  Cursor cursor = getContentResolver().query( 

2.             ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null, 

3.              null, 

4.              null);

复制代码


返回值的内容

_ID

_COUNT

NAME

NUMBER

44

3

Alan Vain

212 555 1234

13

3

Bully Pulpit

425 555 6677

53

3

Rex Cars

201 555 4433

返回值的内容类似上图,不同的content provider会有不同的列和名称,但是会有两个相同的列,上面提到过的一个是_ID,用于唯一标识记录,还有一个_COUNT,用于记录整个结果集的大小,可以看到上面图中的_COUNT的值是相同的。读取返回的数据如果在查询的时候使用到ID,那么返回的数据只有一条记录。在其他情况下,一般会有多条记录。和JDBC的ResultSet类似,需要操作游标遍历结果集,在每行,再通过列名获取到列的值,可以通过getString()、getInt()、getFloat()等方法获取值。比如类似下面:

1.  while (cursor.moveToNext()) { 

2.          builder 

3.                 .append( 

4.                         cursor 

5.                                .getString(cursor 

6.                                       .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)))

7.                 .append("-"); 

8.      }

复制代码


和JDBC中不同,没有直接通过列名获取列值的方法,只能先列名获取到列的整型索引值,然后再通过该索引值定位获取列的值。编辑数据可以通过content provider实现以下编辑功能:

·          增加新的记录;

·          在已经存在的记录中增加新的值;

·          批量更新已经存在的多个记录;

·          删除记录。

所有的编辑功能都是通过ContentResolver的方法实现。一些Content provider对权限要求更严格一些,需要写的权限,如果没有会报错。增加记录要想增加记录到content provider,首先,要在ContentValues对象中设置类似map的键值对,在这里,键的值对应content provider中的列的名字,键值对的值,是对应列希望的类型。然后,调用ContentResolver.insert()方法,传入这个ContentValues对象,和对应Content provider的URI即可。返回值是这个新记录的URI对象。这样你可以通过这个URI获得包含这条记录的Cursor对象。比如:

1.  ContentValues values = new ContentValues();

2.       

3.      values.put(People.NAME, "Abraham Lincoln");

4.       

5.      Uri uri = getContentResolver().insert(People.CONTENT_URI,values);

复制代码


在原有记录上增加值如果记录已经存在,可在记录上增加新的值,或者编辑已经存在的值。首先要过去到原来的值对象,然后要清除原有的值,然后像上面增加记录一样即可:

1.  Uri uri=Uri.withAppendedPath(People.CONTENT_URI,"23");

2.       

3.      Uri phoneUri = Uri.withAppendedPath(uri,People.Phones.CONTENT_DIRECTORY);

4.       

5.      values.clear(); 

6.      values.put(People.Phones.TYPE,People.Phones.TYPE_MOBILE); 

7.      values.put(People.Phones.NUMBER, "1233214567"); 

8.      getContentResolver().insert(phoneUri, values);

复制代码


批量更新值批量更新一组记录的值,比如NY改名为Eew York。可调用ContenResolver.update()方法。删除记录如果是删除单个记录,调用ContentResolver.delete()方法,URI参数,指定到具体行即可。如果是删除多个记录,调用ContentResolver.delete()方法,URI参数指定Content provider即可,并带一个类似SQL的WHERE子句条件。这里和上面类似,不带WHERE关键字。创建自己的Content provider创建content provider,需要:

·          设置存储系统。大多数content provider使用文件或者SQLite数据库,不过你可以用任何方式存储数据。android提供SQLiteoOpenHelper帮助开发者创建和管理SQLiteDatabase。

·          继承ContentProvider,提供对数据的访问。

·          在manifest文件中声明content provider。

继承ContentProvider类必须定义ContentProvider类的子类,需要实现如下方法:query() 
insert() 
update() 
delete() 
getType() 
onCreate()query()方法,返回值是Cursor实例,用于迭代请求的数据。Cursor是一个接口。android为该接口提供了一些只读的(和JDBC的ResultSet不一样,后者还提供可写入的可选特性)Cursor实现。比如SQLiteCursor,可迭代SQLite数据库中的数据。可以通过SQLiteDatabase类的query()方法获取到该Cursor实例。还有其他的Cursor实现,比如MatrixCursor,用于数据不是存储在数据库的情况下。因为Content provider可能被多个ContentResolver对象在不同的进程和线程中调用,因此实现Content provider必须考虑线程安全问题。作为良好的习惯,在实现编辑数据的代码中,要调用ContentResolver.notifyChange()方法,通知那些监听数据变化的监听器。在实现子类的时候,还有一些步骤可以简化Content provider客户端的使用:定义public static final Uri常量,名称为CONTENT_URI:

1.  public static final Uri CONTENT_URI = 

2.                    Uri.parse("content://com.example.codelab.transportationprovider");

复制代码


如果有多个表,它们也是使用相同的CONTENT_URI,只是它们的路径部分不同。
也就是说红色框部分是一致的。定义返回的列名,public staticfinal,列名的值,比如使用SQLite数据库作为存储,对应表的列名。在文档中要写出各个列的数据类型,便于使用者读取。如果需要处理新的MIME数据类型,比如通过Intent的方式,并且带data的mimeType,那么需要在ContentProvider.getType()方法中进行处理。声明Content Provider创建Content Provider后,需要在manifest文件中声明,android系统才能知道它,当其他应用需要调用该Content Provider时才能创建或者调用它。语法类似:

1.  <providerandroid:name="com.easymorse.cp.MyContentProvider" 

2.                 android:authorities="com.easymorse.cp.mycp"></provider>

复制代码


android:name要写ContentProvider继承类的全名。android:authorities要写和CONTENT_URI常量的B部分(见上面图)。注意不要把上图C和D部分加到authorities中去。authorities是用来识别ContentProvider的,C和D部分实际上是ContentProvider内部使用的。

原创粉丝点击