文档章节

android Content Provider 详解

Android攻城狮
 Android攻城狮
发布于 2015/08/19 11:07
字数 2637
阅读 44
收藏 3

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

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

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

创建自己的 Content provider ,需要继承ContentProvider类; 如果你的数据和已存在的Content provider 数据结构一致,可以将数据写到已存在的 Content provider 中,当然前提是获取写该 Content provider 的权限。比如把OA中的成员通讯信息加入到系统的联系人 Contentprovider 中。Content provider 基础

所有Contentprovider都需要实现相同的接口用于查询Contentprovider并返回数据,也包括增加、修改和删除数据。

首先需要获得一个ContentResolver的实例,可通过Activity的成员方法getContentResovler()方法:

ContentResolver cr = getContentResolver();


ContentResolver实例带的方法可实现找到指定的Contentprovider并获取到Contentprovider的数据。

ContentResolver的查询过程开始,Android系统将确定查询所需的具体Contentprovider,确认它是否启动并运行它。android系统负责初始化所有的Contentprovider,不需要用户自己去创建。实际上,contentprovider的用户都不可能直接访问到contentprovider实例,只能通过ContentResolver在中间代理。

数据模型

Contentprovider展示数据类似一个单个数据库表。其中:

每行有个带唯一值的数字字段,名为_ID,可用于对表中指定记录的定位; Content provider 返回的数据结构,是类似JDBC的ResultSet,在android中,是Cursor对象。 URI

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

content://

表示这个uri指定一个contentprovider

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

android.provider.Contacts.Phones.CONTENT_URI
android.provider.Contacts.Photos.CONTENT_URI

要注意的是上面例子中的Contacts,已经在android 2.0及以上版本不赞成使用。

查询 Content provider

要想使用一个contentprovider,需要以下信息:

定义这个 content provider 的URI 返回结果的字段名称 这些字段的数据类型

如果需要查询contentprovider数据集的特定记录(行),还需要知道该记录的ID的值。

构建查询

查询就是输入URI等参数,其中URI是必须的,其他是可选的,如果系统能找到URI对应的contentprovider将返回一个Cursor对象。

可以通过ContentResolver.query()或者Activity.managedQuery()方法。两者的方法参数完全一样,查询过程和返回值也是相同的。区别是,通过Activity.managedQuery()方法,不但获取到Cursor对象,而且能够管理Cursor对象的生命周期,比如当Activity暂停(pause)的时候,卸载该Cursor对象,当Activity restart的时候重新查询。另外,也可以对一个没有处于Activity管理的Cursor对象做成被Activity管理的,通过调用 Activity.startManaginCursor()方法。

类似这样:

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


其中第一个参数myPerson是Uri类型实例。

如果需要查询的是指定行的记录,需要用_ID值,比如ID值为23,URI将是类似:

content://. . . ./23

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

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

或者:

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

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

其他几个参数:

names,可以为null,表示取数据集的全部列,或者声明一个String数组,数组中存放列名称,比如: People._ID 。一般列名都在该 Content provider 中有常量对应; 针对返回结果的过滤器,格式类似于SQL中的WHERE子句,区别是不带WHERE关键字,如果返回null表示不过滤,比如 name=? ; 前面过滤器的参数,是String数组,是针对前面条件中?占位符的值; 排序参数,类似SQL的ORDER BY字句,不过不需要写ORDER BY部分,比如 name desc ,如果不排序,可输入null。

返回值是Cursor对象,游标位置在第一条记录之前。

下面实例适用于android 2.0及以上版本,从android通讯录中得到姓名字段:

Cursor cursor = getContentResolver().query(
        ContactsContract.CommonDataKinds.Phone.
CONTENT_URI, null, null,null,null);


返回值的内容 
 

返回值的内容类似上图,不同的contentprovider会有不同的列和名称,但是会有两个相同的列,上面提到过的一个是_ID,用于唯一标识记录,还有一个_COUNT,用于记录整个结果集的大小,可以看到上面图中的_COUNT的值是相同的。

读取返回的数据

如果在查询的时候使用到ID,那么返回的数据只有一条记录。在其他情况下,一般会有多条记录。和JDBC的ResultSet类似,需要操作游标遍历结果集,在每行,再通过列名获取到列的值,可以通过getString()、getInt()、getFloat()等方法获取值。比如类似下面:

while (cursor.moveToNext()) {
    builder.append(cursor.getString(cursor
               .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)))
                    .append("-");
}


 

和JDBC中不同,没有直接通过列名获取列值的方法,只能先列名获取到列的整型索引值,然后再通过该索引值定位获取列的值。

编辑数据

可以通过contentprovider实现以下编辑功能:

增加新的记录; 在已经存在的记录中增加新的值; 批量更新已经存在的多个记录; 删除记录。

所有的编辑功能都是通过ContentResolver的方法实现。一些Contentprovider对权限要求更严格一些,需要写的权限,如果没有会报错。

增加记录

要想增加记录到contentprovider,首先,要在ContentValues对象中设置类似map的键值对,在这里,键的值对应contentprovider中的列的名字,键值对的值,是对应列希望的类型。然后,调用ContentResolver.insert()方法,传入这个ContentValues对象,和对应Contentprovider的URI即可。返回值是这个新记录的URI对象。这样你可以通过这个URI获得包含这条记录的Cursor对象。比如:

ContentValues values = new ContentValues();
values.put(People.NAME, "Abraham Lincoln");
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);


在原有记录上增加值

如果记录已经存在,可在记录上增加新的值,或者编辑已经存在的值。

首先要过去到原来的值对象,然后要清除原有的值,然后像上面增加记录一样即可:

Uri uri=Uri.withAppendedPath(People.CONTENT_URI, "23");
Uri phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
values.clear();values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);values.put(People.Phones.NUMBER, "1233214567");getContentResolver().insert(phoneUri, values);


批量更新值

批量更新一组记录的值,比如NY改名为Eew York。可调用ContenResolver.update()方法。

删除记录

如果是删除单个记录,调用ContentResolver.delete()方法,URI参数,指定到具体行即可。

如果是删除多个记录,调用ContentResolver.delete()方法,URI参数指定Contentprovider即可,并带一个类似SQL的WHERE子句条件。这里和上面类似,不带WHERE关键字。

创建自己的 Content provider

创建contentprovider,需要:

设置存储系统。大多数 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,用于数据不是存储在数据库的情况下。

因为Contentprovider可能被多个ContentResolver对象在不同的进程和线程中调用,因此实现Contentprovider必须考虑线程安全问题。

作为良好的习惯,在实现编辑数据的代码中,要调用ContentResolver.notifyChange()方法,通知那些监听数据变化的监听器。

在实现子类的时候,还有一些步骤可以简化Contentprovider客户端的使用:

定义public static final Uri常量,名称为CONTENT_URI:

public static final UriCONTENT_URI =
               Uri.parse("content://com.example.codelab.transportationprovider");

如果有多个表,它们也是使用相同的CONTENT_URI,只是它们的路径部分不同。

也就是说红色框部分是一致的。

定义返回的列名,public static final,列名的值,比如使用SQLite数据库作为存储,对应表的列名。

在文档中要写出各个列的数据类型,便于使用者读取。

如果需要处理新的MIME数据类型,比如通过Intent的方式,并且带data的mimeType(参见总结一下Intent概念),那么需要在ContentProvider.getType()方法中进行处理,参见编写完整的Contentprovider示例编写一个getType方法部分。

如果处理数据库表中超大的数据,比如很大的位图文件,一般存在文件系统中,可以参照contentprovider中使用大型二进制文件,这样第三方的contentprovider使用者,可以访问不属于它权限的文件,通过contentprovider做代理。

声明 Content Provider

创建ContentProvider后,需要在manifest文件中声明,android系统才能知道它,当其他应用需要调用该ContentProvider时才能创建或者调用它。

语法类似:

<providerandroid:name="com.easymorse.cp.MyContentProvider"
            android:authorities="com.easymorse.cp.mycp"></provider>

android:name要写ContentProvider继承类的全名。

android:authorities要写和CONTENT_URI常量的B部分(见上面图)。

注意不要把上图C和D部分加到authorities中去。authorities是用来识别ContentProvider的,C和D部分实际上是ContentProvider内部使用的。

本文参考:http://androidappdocs.appspot.com/guide/topics/providers/content-providers.html


本文转载自:

Android攻城狮
粉丝 0
博文 30
码字总数 0
作品 0
朝阳
程序员
私信 提问
android Content Provider 详解

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

鉴客
2011/12/21
39.5K
5
AndroidManifest.xml文件详解(一)

每个应用程序在它的根目录中都必须要有一个AndroidManifest.xml文件。这个清单把应用程序的基本信息提交给Android系统,在应用程序的代码能够运行之前,这个信息系统必须建立。以下是清单文件...

长平狐
2012/10/16
94
0
Android基础 : ContentProvider和getContentResolver

安卓系统中的数据库SqlLite操作和java中mysql的数据库操作很不一样,造成这样的原因是因为在安卓中数据库是属于进程的不存在数据库客户端,也不存在数据库服务器。 关于SqlLite数据库的文章可...

墨梅
2014/10/23
123
0
Android Content Provider 总结思维导图

根据Google Android Doc总结: 1.Content Prodiver的基本概念 2.增删改查 3.创建Content Provider 4.在Manifest.xml中定义 5.Content URI详解 原文出处:http://www.mcjiffy.cn/353.html...

鉴客
2011/10/21
1K
1
android四大组件--ContentProvider详解

一、相关ContentProvider概念解析: 1、ContentProvider简介 在Android官方指出的Android的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。但...

大凉龙雀
2015/10/31
602
0

没有更多内容

加载失败,请刷新页面

加载更多

代理模式之JDK动态代理 — “JDK Dynamic Proxy“

动态代理的原理是什么? 所谓的动态代理,他是一个代理机制,代理机制可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成,通过代理可以有效的让调...

code-ortaerc
今天
5
0
学习记录(day05-标签操作、属性绑定、语句控制、数据绑定、事件绑定、案例用户登录)

[TOC] 1.1.1标签操作v-text&v-html v-text:会把data中绑定的数据值原样输出。 v-html:会把data中值输出,且会自动解析html代码 <!--可以将指定的内容显示到标签体中--><标签 v-text=""></......

庭前云落
今天
8
0
VMware vSphere的两种RDM磁盘

在VMware vSphere vCenter中创建虚拟机时,可以添加一种叫RDM的磁盘。 RDM - Raw Device Mapping,原始设备映射,那么,RDM磁盘是不是就可以称作为“原始设备映射磁盘”呢?这也是一种可以热...

大别阿郎
今天
12
0
【AngularJS学习笔记】02 小杂烩及学习总结

本文转载于:专业的前端网站☞【AngularJS学习笔记】02 小杂烩及学习总结 表格示例 <div ng-app="myApp" ng-controller="customersCtrl"> <table> <tr ng-repeat="x in names | orderBy ......

前端老手
昨天
16
0
Linux 内核的五大创新

在科技行业,创新这个词几乎和革命一样到处泛滥,所以很难将那些夸张的东西与真正令人振奋的东西区分开来。Linux内核被称为创新,但它又被称为现代计算中最大的奇迹,一个微观世界中的庞然大...

阮鹏
昨天
20
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部