Android系统本身提供了大量的ContentProvider,例如联系人信息、系统的多媒体信息等,我们开发的应用程序主要是通过ContentResolver来调用系统的ContentProvider提供的query()、insert()、update()和delete()方法来获取Android内部的数据。
转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空)
一、如何使用ContentResolver操作系统ContentProvider暴露的内部数据?
1.调用Activity的getContentResolver()获取ContentResolver对象;
2.根据需要调用ContentResolver的insert()、delete()、update()和query()方法操作数据。

二、实战源码剖析

1.加入读写权限
<uses-permission android:name="android.permission.READ_CONTACTS" />     //授予读联系人ContentProvider的权限
<uses-permission android:name="android.permission.WRITE_CONTACTS" />   //授予写联系人ContentProvider的权限

2.联系人ContentProvider的Uri

(1)ContactsContract.Contacts.CONTENT_URI(content://com.android.contacts/contacts):管理联系人的Uri;
(2)ContactsContract.CommonDataKinds.Phone.CONTENT_URI(content://com.android.contacts/data/phones):管理联系人的电话的Uri;
(3)ContactsContract.CommonDataKinds.Email.CONTENT_URI(content://com.android.contacts/data/emails ):管理联系人的E-Mail的Uri。

3.

取得所有联系人的表的Cursor对象:
1)ContentResolver contentResolver=getContentResolver();//获取 ContentResolver对象查询在ContentProvider里定义的共享对象;
2)Cursor cursor=contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);//根据URI对象ContactsContract.Contacts.CONTENT_URI查询所有联系人

4.获取指定联系人(_ID)的具体信息

    从Cursor对象里我们关键是要取得联系人的_id。通过它,再通过ContactsContract.CommonDataKinds的各个子类查询该_id联系人的电话(ContactsContract.CommonDataKinds.Phone),email(ContactsContract.CommonDataKinds.Email)等等。
以取得该联系人所有电话为例:
1)int idFieldIndex=cursor.getColumnIndex(ContactsContract.Contacts._ID);
      int id=cursor.getInt(idFieldIndex);//根据列名取得该联系人的id;
2)Cursor phonecursor=contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"="+id, null, null);//再类ContactsContract.CommonDataKinds.Phone中根据查询相应id联系人的所有电话;
    类似地可以ContactsContract.CommonDataKinds的不同的子类查询不同的内容。android文档告诉我们推荐使用ContactsContract.Contacts.LOOKUP_KEY代替ContactsContract.Contacts._ID。

升华笔记:
1.Cursor接口
(1)功能概述:该接口主要用于随机读写从数据库查询返回的set集合形式的结果。

(2)常用方法:
abstract void close() :关闭Cursor,释放所有资源
abstract boolean moveToNext() :将游标移动到下一行

String getString(int columnIndex):将指定的列索引对应的值转换为字符串返回

int getColumnIndex(String columnName) :返回给定列名对应的列索引值(为0开始的整型值),传入参数为列名;
2.由于读取联系人比较的占用资源,为了提高用户的体验度。考虑将读取的过程放在线程里完成,推荐使用AsyncTask类。
取Android系统的通讯录时一般会先读取联系人然后再读取其号码,嵌套循环读取。如果通讯录人数不多速度尚可,但是通讯录里有1-2百人恐怕就比较慢了,如果硬件再差点体验就更差了。可以使用
ContactsContract.CommonDataKinds.Phone.CONTENT_URI(对应contacts2.db的数据视图view_data_restricted)视图来读取避免嵌套读取,而对于PhoneLookup.CONTENT_FILTER_URI确不能直接使用

4.联系人的API

(1)ContactsContract[public final class]
    java.lang.Object
                   ↳ android.provider.ContactsContract
    ContactsContract是联系人数据提供者和应用程序的契约书,它定义所有支持的Content Provider URI和数据列。 从Android 2.0(API Level 5)开始,Android平台提供了一个改进的Contacts API,以适应一个联系人可以有多个帐户的需求,比如说手机通讯录和GMAIL通讯录,两个通讯录中的两条记录可以是同一个人。新的Contacts API主要是由ContactsContract及其相关的类来管理,旧的API(android.provider.Contacts)已不赞成使用,但为了兼容仍可以使用,只不过像以前一样,只能返回第一个帐户的信息。
   在新的Contacts API中,联系人数据被放到三张表中:Contacts、RawContacts和Data。这样可以帮助系统更好地存储与管理一个联系人的多个帐户的信息。
ContactsContract表结构介绍:

A.ContactsContract.Data 用于保存个人数据,例如电话号码、邮件、手机铃声、即时通讯方式、照片等等
B.ContactsContract.RawContacts 用于关联联系人信息与账号,因为有可能手机的联系人信息是从不同的Gmail或者其他地方导入的,为互相区别并方便同步,特引入账号概念。
C.ContactsContract.Contacts 属于不同账号下的某联系人信息可能描述的是同一个人,这张表就是RawContacts的并集,如果某联系人信息被修改,和它描述同一个人信息肯定也要做相应的更新。

(2)ContactsContract.Data

    Data表存储了联系人的详细信息,表中的每一行存储一个特定类型的信息,比如Email、Address或Phone。每一行通过一个mimetype_id的字段来表示该行存储的是什么类型的数据,该字段引用了mimetyps表,此表存储了常用的数据类型。Data表的字段主要有:
mimetype_id :表示该行存储的信息的类型 
raw_contact_id: 表示该行所属的RawContact
is_primary :多个data数据组成一个raw contact,该字段表示此data是否是其所属的raw contact的主data,即其display name会作为raw contact的display name
is_super_primary :该data是否是其所属的contact的主data,如果is_super_primary为1则is_primary一定为1
data1~data15 :15个数据字段,对于不同类型的信息,表示不同的含义,ContactsContract.CommomDataKinds类中定义了与常用的数据类型相对应的一些类,这些类中分别定义了相应数据类型中这些字段表示的含义。一般data1表是主信息(如电话,Email地址等),data2表示副信息,data15表示Blob数据。
data_sync1~data_sync4 sync_adapter:要用的字段(sync_adapter用于数据的同步,比如你手机中的Gmail帐户与Google服务器的同步)。
data_version :数据的版本,用于数据的同步。
    Data类中的Data.CONTACT_ID与Data.RAW_CONTACT_ID分别表示该表项对应的联系人在Contact与RawContract表中的ID,我们只需要知道某一个联系人的contactId或rawContractId,并根据其查找的数据的类型就可以查到相应类型的信息。

(3)ContactsContract.RawContact

    RawContact表中的一行存储Data表中一些数据行的集合及一些其他的信息,表示一个联系人某一特定帐户的信息,比如Facebook或Exchange的一个联系人。 当插入一个raw contact或当一个raw contact所属的一个data改变时,系统会检查这个raw contact跟其他的raw contact是否可以匹配(比如如果两个raw contact的data包含相同的电话号码或名字),如果匹配他们就会被综合到一起,也就是说他们会属于同一个cantact,表现为在RawContact表中他们引用的cantact_id是一样的。 联系人姓名、组织、电话号码、Email或昵称的改变会引发raw contact的重新聚合。有两个方法控制聚合的行为Aggregaton Mode与ContactsContract.AggregationExceptions。 
Aggregaton Mode:
RawContact表中有一个字段aggregation_mode,通过向特定raw contact行中插入这个字段可以修改系统对这个raw contact的聚合行为,其允许的值如下:AGGREGATION_MODE_DEFAULT:正常模式,允许自动聚合;
AGGREGATION_MODE_DISABLE:不允许聚合;
AGGREGATION_MODE_SUSPENDED:当一个raw contact的aggregation mode修改为suspended时,如果其已是一个已聚合的contact的一部分,那么它仍会保持与原来聚合到一起的raw contact的关系,即使它已改变不再跟其他raw contact匹配。
ContactsContract.AggregationExceptions:
在数据库中存在一个表:agg_exceptions。通过字段raw_contact_id1、raw_contact_id2、mode存储两个raw contact聚合的方法,系统定义的聚合行为有3个:
TYPE_AUTOMATIC=0  由系统决定聚合行为,默认值。
TYPE_KEEP_SEPARATE=2  不聚合
TYPE_KEEP_TOGETHER=1  聚合

(4)ContactsContract.Contact

Contact表中的一行表示一个联系人,它是RawContact表中的一行或多行的数据的组合,这些RawContact表中的行表示同一个人的不同的帐户信息。Contact中的数据由系统组合RawContact表中的数据自动生成。 不可以直接向这个表中插入数据,当一个raw contact被插入的时候,系统会首先查找Contact表看是否有记录跟插入的raw contact表示同一个人,如果找到了,则把找到的这个contact的_ID插入raw contact记录的CONTACT_ID字段,如果没有找到,则系统自动插入一个Contact记录并把它的_ID插入新插入的raw contact的CONTACT_ID列。 
Contact表中只有TIMES_CONTACTED、LAST_TIME_CONTACTED、STARRED、CUSTOM_RINGTONE、SENE_TO_VOICEMAIL列可更改,这些列的更改会导致相应的raw contact被更改。 当删除Contact表中的记录时,会删除一个联系人的所有帐户的信息,也就是说,其对应的所有raw contacts也会被删除,各raw contact对应的data也就被删除了,sync adapter同步时也会删除服务器端的相应记录。 
如果需要读取一个联系人的信息用CONTENT_LOOKUP_RUI代替CONTENT_URI(见后面);
如果需要通过电话号码查找一个联系人,用PhoneLookup.CONTENT_FIILTER_URI,这个URI为这个目的进行了优化;
如果需要通过部分名字的匹配查找,用CONTENT_FILTER_URI;
如果需要通过email,address等信息查找,查找表ContactsContract.Data,结果包含contact ID,名字...
    ContactsContract的子类ContactsContract.Contacts是一张表,代表了所有联系人的统计信息。比如联系人ID(—ID),查询键(LOOKUP_KEY),联系人的姓名(DISPLAY_NAME_PRIMARY),头像的id(PHOTO_ID)以及群组的id等等。

(5)ContactsContract.CommonDataKinds类

在前面讲Data表的结构时讲到,Data的data1~data15字段用于存储各类型的数据信息,那么这15个字段分别表示什么信息呢?
前面提到了,Data表中有一个mimetype_id字段,通过这个字段关联mimetypes表表示该行代表的信息类型,因为Data表中的每一行可以表示如Phone或Address等不同类型的信息,所以对于不同类型的信息,data1~data15这15列表示不同的含义,如果要靠记忆记住这15列对于特定的类型分别表示什么意义自然不行,于是Google就预定义了一些类,每一个类对应一些预先定义好的数据类型,在每个类中定义了一些语义地、方便记忆的常量,用来对应这15个字段,比如在CommonDataKinds.Email类中有如下定义
publicstaticfinal String ADDRESS = DATA1;
publicstaticfinal String DISPLAY_NAME = DATA4;
DATA1与DATA4为继承自DataColumns中的常量,在DataColumns中是这样定义的:
publicstaticfinal String DATA1 = "data1";
publicstaticfinal String DATA4 = "data4";
这样,当于们要查找Email地址时,只需要通过ContactsContract.CommonDataKinds.Email.ADDRESS引用,而不需要知道它是存储在Data表中的data1列中。

三、源码

public class ContactProviderTest extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Button search=(Button)findViewById(R.id.searchBtn);/*查询联系人*/search.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {//1.定义两个List来封装系统的联系人信息,指定联系人的电话号码、Email等详情final ArrayList<String> names=new ArrayList<String>();final ArrayList<ArrayList<String>> details=new ArrayList<ArrayList<String>>();//2.使用ContentResolver查找联系人数据(Query the given URI, returning a Cursor over the result set)Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);//3.遍历查询结果,获取系统中所有联系人while(cursor.moveToNext()){//a.获取联系人IDString contactId=cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));//b.获取联系人的名字String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));names.add(name);  //将名字依次添加到List列表中/*-----------------------------------------------------------------------------------------------------*///c.使用ContentResolver查找联系人的电话号码Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null, null);//d.遍历查询结果,获取该联系人的多个电话号码ArrayList<String> detail = new ArrayList<String>();while(phones.moveToNext()){String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));detail.add("电话号码"+phoneNumber);}phones.close();/*-----------------------------------------------------------------------------------------------------*///e.使用ContentResolver查找联系人的E-Mail地址Cursor emails = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,ContactsContract.CommonDataKinds.Email.CONTACT_ID+"="+contactId, null, null);//f.遍历查询结果,获取该联系人的多个E-Mail地址while(emails.moveToNext()){String emailAddress = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));detail.add("邮件地址:"+emailAddress);}emails.close();details.add(detail);}cursor.close();/*-----------------------------------------------------------------------------------------------------*///4.加载result.xmlView resultDialog = getLayoutInflater().inflate(R.layout.result, null);//5.获取resultDialog中ID为list的ExpandableListViewExpandableListView list = (ExpandableListView) resultDialog.findViewById(R.id.list);//6.创建一个ExpandableListAdapter对象ExpandableListAdapter adapter = new BaseExpandableListAdapter(){//a.获取指定组位置、指定子列表项处的子列表项数据@Overridepublic Object getChild(int groupPosition, int childPosition) {return details.get(groupPosition).get(childPosition);}@Overridepublic long getChildId(int groupPosition, int childPosition) {return childPosition;}@Overridepublic int getChildrenCount(int groupPosition) {return details.get(groupPosition).size();}private TextView getTextView(){AbsListView.LayoutParams lp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 64);TextView textView = new TextView(ContactProviderTest.this);textView.setLayoutParams(lp);textView.setGravity(Gravity.CENTER_VERTICAL|Gravity.LEFT);textView.setPadding(36, 0, 0, 0);textView.setTextSize(20);return textView;}//b.该方法决定每个子选项的外观@Overridepublic View getChildView(int groupPosition,int childPosition, boolean isLastChild,View convertView, ViewGroup parent){TextView textView = getTextView();textView.setText(getChild(groupPosition,childPosition).toString());return textView;}//c.获取指定组位置处的组数据@Overridepublic Object getGroup(int groupPosition) {return names.get(groupPosition);}@Overridepublic int getGroupCount() {return names.size();}@Overridepublic long getGroupId(int groupPosition) {return groupPosition;}//d.该方法决定每个组选项的外观@Overridepublic View getGroupView(int groupPosition,boolean isExpanded, View convertView,ViewGroup parent) {TextView textView = getTextView();textView.setText(getGroup(groupPosition).toString());return textView;}@Overridepublic boolean isChildSelectable(int groupPosition,int childPosition) {return true;}@Overridepublic boolean hasStableIds() {return true;}};//7.为ExpandableListView设置Adapter对象list.setAdapter(adapter);//8.使用对话框来显示查询结果new AlertDialog.Builder(ContactProviderTest.this).setView(resultDialog).setPositiveButton("确定",null).show();}});/*添加联系人*/Button add=(Button)findViewById(R.id.addBtn);add.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {//a.获取程序界面中的三个文本框String name = ((EditText)findViewById(R.id.name)).getText().toString();String phone = ((EditText)findViewById(R.id.phone)).getText().toString();String email = ((EditText)findViewById(R.id.email)).getText().toString();//b.创建一个空的ContentValuesContentValues values = new ContentValues();//c.向RawContacts.CONTENT_URI执行一个空值插入吗,目的是获取系统返回的rawContactIdUri rawContactUri = getContentResolver().insert(RawContacts.CONTENT_URI, values);long rawContactId=ContentUris.parseId(rawContactUri);values.clear();/*-----------------------------------------------------*/values.put(Data.RAW_CONTACT_ID, rawContactId);//d.设置内容类型values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);//e.设置联系人名字values.put(StructuredName.GIVEN_NAME, name);//f.向联系人URI添加联系人名字getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);values.clear();/*-----------------------------------------------------*/values.put(Data.RAW_CONTACT_ID, rawContactId);values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);//g.设置联系人的电话号码values.put(Phone.NUMBER, phone);//h.设置电话类型values.put(Phone.TYPE, Phone.TYPE_MOBILE);//i.向联系人电话号码URI添加电话号码getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);values.clear();/*-----------------------------------------------------*/values.put(Data.RAW_CONTACT_ID, rawContactId);values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);//j.设置联系人的E-mail地址values.put(Email.DATA, email);//k.设置该电子邮件的类型values.put(Email.TYPE, Email.TYPE_WORK);//l.向联系人E-mail URI添加E-mail数据getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);Toast.makeText(ContactProviderTest.this,"联系人数据添加成功", Toast.LENGTH_SHORT).show();}});}}

效果如下 :
参考:
http://blog.163.com/hesky_fly/blog/static/732868692011102311551131/
http://wear.techbrood.com/reference/android/provider/ContactsContract.Data.html

Android学习笔记十七.使用ContentProvider实现数据共享(四).操作系统(联系人)的ContentProvider相关推荐

  1. Android学习笔记(十)——Activity的四种启动模式

    Standard标准模式 每启动一个Activity就创建一个新的实例,不管这个activity之前是否启动过,都创建一个新的 singleTop栈顶复用模式 singleTask栈内复用模式 sin ...

  2. Android学习笔记:Android基础知识点(不断更新中)

    1.Android学习笔记:OkHttp 2.Android学习笔记:更新UI的方法(UI线程和非UI线程) 3.Android学习笔记:Volley 4.Android学习笔记:Handler 5. ...

  3. Android学习笔记36:使用SQLite方式存储数据

    在Android中一共提供了5种数据存储方式,分别为: (1)Files:通过FileInputStream和FileOutputStream对文件进行操作.具体使用方法可以参阅博文<Andro ...

  4. Android学习笔记(四九):通过Content Provider访问数据

    在上次笔记中,我们编写了自己的Provider,这次笔记,我们将通过Content Provider的Uri接口对数据进行访问,重写Android学习笔记(四二)中例子.在这里我们不在充分描述相关UI ...

  5. Android学习笔记之AndroidManifest.xml文件解析(摘自皮狼的博客)

    Android学习笔记之AndroidManifest.xml文件解析 一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件.它 ...

  6. Android学习笔记21:ImageView获取网络图片

    Android平台有3种网络接口可以使用,它们分别是:java.net.*(标准java接口).org.apache(Apache接口)和android.net.*(Android网络接口).本文将使 ...

  7. Android学习笔记(七):多个Activity和Intent

    根据www.mars-droid.com:Andriod开发视频教学,先跳过书本<Beginning Android 2>的几个章,我是这两个资源一起看,需要进行一下同步.先初步了解一下应 ...

  8. Android学习笔记26:图片切换控件ImageSwitcher的使用

    在Windows操作系统中,要查看多张图片,可以通过使用"Windows照片查看器"在"上一张"和"下一张"之间切换,进行多张图片的浏览. ...

  9. Pro Android学习笔记(二九):用户界面和控制(17):include和merge

    xml控件代码重用:include 如果我们定义一个控件,需要在不同的layout中重复使用,或者在同一个layout中重复使用,可以采用include的方式.例如定义my_button.xml如下 ...

最新文章

  1. 企业网络推广——企业网络有推广专员如何做好基本的网站优化布局
  2. Java开发SVM之Eclipse集成LibSVM示例
  3. Hystrix配置参数查找方式
  4. [转]JVM运行时内存结构
  5. 为什么中国天才都往美国跑,可美国人的数学那么槽糕
  6. Servlet读取文件的最好的方式
  7. AC日记——Mato的文件管理 bzoj 3289
  8. HomeBrew 更换为国内源--提高brew命令操作速度
  9. 高性能MySQL(第二版)第三章 架构优化和索引(上)——读书笔记
  10. NSTimer 的正确用法你真的知道吗?
  11. 编程十年 (6):虚荣的C
  12. JavaScript判断数组的方法
  13. 宏先生说:口罩和额温枪的套路!
  14. 【JZOJ1320】拯救奶牛
  15. grafana配置alert
  16. Linux系统启动分析
  17. STM32 ISP烧录过程
  18. 谭民机器人_机器人技术研究进展_谭民
  19. 世界级3D渲染大赛TOP3大佬们的制作流程大揭秘!
  20. LInux sed命令详解

热门文章

  1. 计算机应用基础在线3,计算机应用基础3(答案).doc
  2. 程序员真实从零开始实操 赚钱渠道之二 群控+极速版
  3. 2019南航计算机学院夏令营,2019南航年度人物宣讲丨计算机学院开展线上宣讲活动...
  4. pdf怎么分割成一页一页的文件?
  5. 网易惠惠购物助手:大数据实时更新框架概述
  6. drep:微生物基因组快速去冗余-文章解读+帮助文档+实战
  7. 个人常用多媒体软件方案
  8. 杰理AC692X---添加串口打印调试
  9. 浅谈IP数据报分片与重组
  10. MaskRcnn ImportError: _C.cpython-37m-x86_64-linux-gnu.so: undefined symbol:XXX