另参考博文:使用ContactsContract API

android联系人详解

靡不有初,鮮克有終。《詩經》

很多事情,绝大多数人都会在开始的时候满怀热情,而能坚持到底的却是寥寥无几。对待自己的目标,虎头蛇尾绝不可取,半途而废只会一无所成,我们必须持之以恒的做下去,坚持到底才能摘取胜利的果实。最近也忙了起来,忙着给自己充电,深知这项任务的艰巨,不是一天两天的事,所以也借用这句警言来告诫自己,坚持不懈的走下去。

今天我们来讲解一下如何利用ContentProvider机制读写联系人信息。

在Android中,ContentProvider是一种数据包装器,适合在不同进程间实现信息的共享。例如,在Android中SQLite数据库是一个典型的数据源,我们可以把它封装到ContentProvider中,这样就可以很好的为其他应用提供信息共享服务。其他应用在访问ContentProvider时,可以使用一组类似REST的URI的方式进行数据操作,大大简化了读写信息的复杂度。例如,如果要从封装图书数据库的ContentProvider获取一组图书,需要使用类似以下形式的URI:

content://com.scott.book.BookProvider/books

而要从图书数据库中获取指定图书(比如23号图书),需要使用类似以下形式的URI:

content://com.scott.book.BookProvider/books/23

注:ContentProvider是一个抽象类,定义了一系列操作数据的方法模板,BookProvider需要实现这些方法,实现图书信息的各种操作。

那么,现在知道了具体的URI之后,我们又如何操作进而取得数据呢?

此时,我们就要了解ContentResolver这个类,它跟ContentProvider是对应的关系,我们正是通过它来与ContentProvider进行数据交换的。android.content.Context类为我们定义了getContentResolver()方法,用于获取一个ContentResolver对象,如果我们在运行期可以通过getContext()获取当前Context实例对象,就可以通过这个实例对象所提供的getContentResolver()方法获取到ContentResolver类型的实例对象,进而可以操作对应的数据。

下面我们就通过联系人实例对这种机制进行演示。

在Android中,联系人的操作都是通过一个统一的途径来读写数据的,我们打开/data/data/com.android.providers.contacts可以看到联系人的数据源:

有兴趣的朋友可以导出这个文件,用专业的工具软件打开看一下表结构。

对这个SQLite类型的数据源的封装后,联系人就以ContentProvider的形式为其他应用进程提供联系人的读写服务,我们就可以顺利成章的操作自己的联系人信息了。

为了方便测试,我们先添加两个联系人到数据源中,如图所示:

我们看到,每个联系人都有两个电话号码和两个邮箱账号,分别为家庭座机号码、移动手机号码、家庭邮箱账号和工作邮箱账号。当然在添加联系人时有很多其他信息,我们这里都没有填写,只选择了最常用的电话和邮箱,主要是方便演示这个过程。

在演示代码之前,我们需要了解一下android.provider.ContactsContract这个类(注:在较早的版本中是android.provider.Contacts这个类,不过现在已被废弃,不建议使用),它定义了各种联系人相关的URI和每一种类型信息的属性信息:

有兴趣的朋友还可以读一下源代码,不过比较多,而且内部类使用的特别多,读起来有一定的困难,还是要做好心理准备。

下面我们通过一个项目,来演示一下联系人操作的具体过程。新建一个名为provider的项目,创建一个名为ContactsReadTest的测试用例,如下:

[java]  view plain copy
  1. package com.scott.provider;
  2. import java.util.ArrayList;
  3. import android.content.ContentResolver;
  4. import android.database.Cursor;
  5. import android.net.Uri;
  6. import android.provider.ContactsContract;
  7. import android.test.AndroidTestCase;
  8. import android.util.Log;
  9. public class ContactsReadTest extends AndroidTestCase {
  10. private static final String TAG = "ContactsReadTest";
  11. //[content://com.android.contacts/contacts]
  12. private static final Uri CONTACTS_URI = ConContactsContract.contacts.CONTENT_URI;
  13. //[content://com.android.contacts/data/phones]
  14. private static final Uri PHONES_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
  15. //[content://com.android.contacts/data/emails]
  16. private static final Uri EMAIL_URI = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
  17. private static final String _ID = ContactsContract.Contacts._ID;
  18. private static final String DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME;
  19. private static final String HAS_PHONE_NUMBER = ContactsContract.Contacts.HAS_PHONE_NUMBER;
  20. private static final String CONTACT_ID = ContactsContract.Data.CONTACT_ID;
  21. private static final String PHONE_NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;
  22. private static final String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.TYPE;
  23. private static final String EMAIL_DATA = ContactsContract.CommonDataKinds.Email.DATA;
  24. private static final String EMAIL_TYPE = ContactsContract.CommonDataKinds.Email.TYPE;
  25. public void testReadContacts() {
  26. ContentResolver resolver = getContext().getContentResolver();
  27. Cursor c = resolver.query(CONTACTS_URI, null, null, null, null);
  28. while (c.moveToNext()) {
  29. int _id = c.getInt(c.getColumnIndex(_ID));
  30. String displayName = c.getString(c.getColumnIndex(DISPLAY_NAME));
  31. Log.i(TAG, displayName);
  32. ArrayList<String> phones = new ArrayList<String>();
  33. ArrayList<String> emails = new ArrayList<String>();
  34. String selection = CONTACT_ID + "=" + _id;  //the 'where' clause
  35. //获取手机号
  36. int hasPhoneNumber = c.getInt(c.getColumnIndex(HAS_PHONE_NUMBER));
  37. if (hasPhoneNumber > 0) {
  38. Cursor  phc = resolver.query(PHONES_URI, null, selection, null, null);
  39. while (phc.moveToNext()) {
  40. String phoneNumber = phc.getString(phc.getColumnIndex(PHONE_NUMBER));
  41. int phoneType = phc.getInt(phc.getColumnIndex(PHONE_TYPE));
  42. phones.add(getPhoneTypeNameById(phoneType) + " : " + phoneNumber);
  43. }
  44. phc.close();
  45. }
  46. Log.i(TAG, "phones: " + phones);
  47. //获取邮箱
  48. Cursor emc = resolver.query(EMAIL_URI,null, selection, null, null);
  49. while (emc.moveToNext()) {
  50. String emailData = emc.getString(emc.getColumnIndex(EMAIL_DATA));
  51. int emailType = emc.getInt(emc.getColumnIndex(EMAIL_TYPE));
  52. emails.add(getEmailTypeNameById(emailType) + " : " + emailData);
  53. }
  54. emc.close();
  55. Log.i(TAG, "emails: " + emails);
  56. }
  57. c.close();
  58. }
  59. private String getPhoneTypeNameById(int typeId) {
  60. switch (typeId) {
  61. case ContactsContract.CommonDataKinds.Phone.TYPE_HOME: return "home";
  62. case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE: return "mobile";
  63. case ContactsContract.CommonDataKinds.Phone.TYPE_WORK: return "work";
  64. default: return "none";
  65. }
  66. }
  67. private String getEmailTypeNameById(int typeId) {
  68. switch (typeId) {
  69. case ContactsContract.CommonDataKinds.Email.TYPE_HOME: return "home";
  70. case ContactsContract.CommonDataKinds.Email.TYPE_WORK: return "work";
  71. case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: return "other";
  72. default: return "none";
  73. }
  74. }
  75. }

为了使这个测试用例运行起来,我们需要在AndroidManifest.xml中配置一下测试设备的声明,它与<application>元素处于同一级别位置:

[html]  view plain copy
  1. <!-- 配置测试设备的主类和目标包 -->
  2. <instrumentation android:name="android.test.InstrumentationTestRunner"
  3. android:targetPackage="com.scott.provider"/>

然后再配置使用测试类库声明,它与<activity>元素处于同一级别位置:

[html]  view plain copy
  1. <!-- 配置测试要使用的类库 -->
  2. <uses-library android:name="android.test.runner"/>

最后,还有一个重要的声明需要配置,就是读取联系人权限,声明如下:

[html]  view plain copy
  1. <!-- 读取联系人 -->
  2. <uses-permission android:name="android.permission.READ_CONTACTS"/>

经过以上准备工作,这个测试用例就可以运转起来了,我们运行一下testReadContacts()方法,打印结果如下:

看来联系人里的信息都被我们准确无误的读取出来了。

如果我们在一个Activity里运行读取联系人的代码,不仅可以使用ContentResolver直接进行读取操作(即查询),还可以使用Activity提供的managedQuery方法方便的实现同样的效果,我们来看一下这个方法的具体代码:

[java]  view plain copy
  1. public final Cursor managedQuery(Uri uri,
  2. String[] projection,
  3. String selection,
  4. String[] selectionArgs,
  5. String sortOrder)
  6. {
  7. Cursor c = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
  8. if (c != null) {
  9. startManagingCursor(c);
  10. }
  11. return c;
  12. }

我们发现,其实它还是使用了ContentResolver进行查询操作,但是多了一步startManagingCursor的操作,它会根据Activity的生命周期对Cursor对象进行管理,避免了一些因Cursor是否释放引起的问题,所以非常方便,大大简化了我们的工作量。

接下来我们将要尝试将一个联系人信息添加到系统联系人的数据源中,实现对联系人的写入操作。我们新建一个名为ContactsWriteTest的测试用例,如下:

[java]  view plain copy
  1. package com.scott.provider;
  2. import java.util.ArrayList;
  3. import android.content.ContentProviderOperation;
  4. import android.content.ContentProviderResult;
  5. import android.content.ContentResolver;
  6. import android.net.Uri;
  7. import android.provider.ContactsContract;
  8. import android.test.AndroidTestCase;
  9. import android.util.Log;
  10. public class ContactsWriteTest extends AndroidTestCase {
  11. private static final String TAG = "ContactsWriteTest";
  12. //[content://com.android.contacts/raw_contacts]
  13. private static final Uri RAW_CONTACTS_URI = ContactsContract.RawContacts.CONTENT_URI;
  14. //[content://com.android.contacts/data]
  15. private static final Uri DATA_URI = ContactsContract.Data.CONTENT_URI;
  16. private static final String ACCOUNT_TYPE = ContactsContract.RawContacts.ACCOUNT_TYPE;
  17. private static final String ACCOUNT_NAME = ContactsContract.RawContacts.ACCOUNT_NAME;
  18. private static final String RAW_CONTACT_ID = ContactsContract.Data.RAW_CONTACT_ID;
  19. private static final String MIMETYPE = ContactsContract.Data.MIMETYPE;
  20. private static final String NAME_ITEM_TYPE = ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE;
  21. private static final String DISPLAY_NAME = ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME;
  22. private static final String PHONE_ITEM_TYPE = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
  23. private static final String PHONE_NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;
  24. private static final String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.TYPE;
  25. private static final int PHONE_TYPE_HOME = ContactsContract.CommonDataKinds.Phone.TYPE_HOME;
  26. private static final int PHONE_TYPE_MOBILE = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE;
  27. private static final String EMAIL_ITEM_TYPE = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
  28. private static final String EMAIL_DATA = ContactsContract.CommonDataKinds.Email.DATA;
  29. private static final String EMAIL_TYPE = ContactsContract.CommonDataKinds.Email.TYPE;
  30. private static final int EMAIL_TYPE_HOME = ContactsContract.CommonDataKinds.Email.TYPE_HOME;
  31. private static final int EMAIL_TYPE_WORK = ContactsContract.CommonDataKinds.Email.TYPE_WORK;
  32. private static final String AUTHORITY = ContactsContract.AUTHORITY;
  33. public void testWriteContacts() throws Exception {
  34. ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
  35. ContentProviderOperation operation = ContentProviderOperation.newInsert(RAW_CONTACTS_URI)
  36. .withValue(ACCOUNT_TYPE, null)
  37. .withValue(ACCOUNT_NAME, null)
  38. .build();
  39. operations.add(operation);
  40. //添加联系人名称操作
  41. operation = ContentProviderOperation.newInsert(DATA_URI)
  42. .withValueBackReference(RAW_CONTACT_ID, 0)
  43. .withValue(MIMETYPE, NAME_ITEM_TYPE)
  44. .withValue(DISPLAY_NAME, "Scott Liu")
  45. .build();
  46. operations.add(operation);
  47. //添加家庭座机号码
  48. operation = ContentProviderOperation.newInsert(DATA_URI)
  49. .withValueBackReference(RAW_CONTACT_ID, 0)
  50. .withValue(MIMETYPE, PHONE_ITEM_TYPE)
  51. .withValue(PHONE_TYPE, PHONE_TYPE_HOME)
  52. .withValue(PHONE_NUMBER, "01034567890")
  53. .build();
  54. operations.add(operation);
  55. //添加移动手机号码
  56. operation = ContentProviderOperation.newInsert(DATA_URI)
  57. .withValueBackReference(RAW_CONTACT_ID, 0)
  58. .withValue(MIMETYPE, PHONE_ITEM_TYPE)
  59. .withValue(PHONE_TYPE, PHONE_TYPE_MOBILE)
  60. .withValue(PHONE_NUMBER, "13034567890")
  61. .build();
  62. operations.add(operation);
  63. //添加家庭邮箱
  64. operation = ContentProviderOperation.newInsert(DATA_URI)
  65. .withValueBackReference(RAW_CONTACT_ID, 0)
  66. .withValue(MIMETYPE, EMAIL_ITEM_TYPE)
  67. .withValue(EMAIL_TYPE, EMAIL_TYPE_HOME)
  68. .withValue(EMAIL_DATA, "scott@android.com")
  69. .build();
  70. operations.add(operation);
  71. //添加工作邮箱
  72. operation = ContentProviderOperation.newInsert(DATA_URI)
  73. .withValueBackReference(RAW_CONTACT_ID, 0)
  74. .withValue(MIMETYPE, EMAIL_ITEM_TYPE)
  75. .withValue(EMAIL_TYPE, EMAIL_TYPE_WORK)
  76. .withValue(EMAIL_DATA, "scott@msapple.com")
  77. .build();
  78. operations.add(operation);
  79. ContentResolver resolver = getContext().getContentResolver();
  80. //批量执行,返回执行结果集
  81. ContentProviderResult[] results = resolver.applyBatch(AUTHORITY, operations);
  82. for (ContentProviderResult result : results) {
  83. Log.i(TAG, result.uri.toString());
  84. }
  85. }
  86. }

在上面的代码中,我们把整个操作分为几个ContentProviderOperation操作,并将他们做批处理操作,我们也许注意到,从第二个操作开始,每一项都有一个withValueBackReference(RAW_CONTACT_ID, 0)步骤,它参照了第一项操作新添加的联系人的id,因为是批处理,我们插入数据前并不知道id的值,不过这个不用担心,在进行批处理插入数据时,它会重新引用新的id值,不会影响最终的结果。

当然,这个也不能忘了配置写入联系人的权限声明:

[html]  view plain copy
  1. <!-- 写入联系人 -->
  2. <uses-permission android:name="android.permission.WRITE_CONTACTS" />

经过以上步骤之后,我们运行一下testWriteContacts()方法,看看联系人是否添加进去了:

关于ContactsContract相关推荐

  1. ContactsContract.Contacts与ContactsContract.CommonDataKinds.Phone的区别

    ContactsContract.Contacts: Constants for the contacts table, which contains a record per aggregate o ...

  2. Android通讯录查询篇--ContactsContract.Data 二(续)

    这次主要要做的就是根据姓名来查找电话,并且加强对通讯录的理解. 以前做一些用到数据库的东西的时候,可能光看代码也是不好联系起各个数据之间的关系,所以我先想到的还是数据库.幸运的是,它还真是一个数据库. ...

  3. ContactsContract

    自Android 2.0(API Level 5)开始,Android平台采用了改进后的Contacts API- ContactsContract,用于管理和集成来自多账户和多数据来源的联系人信息. ...

  4. ContactsContract.Contacts之sort_key

    // 从Contacts表中找出所有联系人 Cursor cursor = context.getContentResolver().query(         ContactsContract.C ...

  5. 使用ContactsContract APIhttp://www.cnblogs.com/carbs/archive/2012/07/16/2593295.html

    转自:http://www.cnblogs.com/carbs/archive/2012/07/16/2593295.html 自Android 2.0(API Level 5)开始,Android平 ...

  6. Android通讯录查询篇--ContactsContract.Data 二

    2019独角兽企业重金招聘Python工程师标准>>> 上次说Data.MIMETYPE这个玩意很重要,官方的文档中描述了一大段的话.里面比较重要的几点是: 1.在Data中有一大段 ...

  7. android获取手机通讯录

    在android中读取联系人信息的程序,包括读取联系人姓名.手机号码和邮箱 (转载自博客:http://www.cnblogs.com/error404/archive/2013/03/12/2956 ...

  8. android 联系人编辑界面,android – 以编程方式编辑联系人的姓名/电话号码

    我试图以编程方式修改联系人的显示名称: try { ArrayList ops = new ArrayList(); ops.add(ContentProviderOperation.newUpdat ...

  9. 关于《Android应用开发揭秘》中早期版本中联系人例子错误的解决办法

    发现目前还有人问这个问题,就在这里在说明一下. 针对于android应用开发揭秘书中的4-5节代码不能工作的补充 首先感谢该篇文章的作者,因为网上到处转载,就不知道原作者了,在这里感谢你对本书的关于与 ...

最新文章

  1. 200kb以上图片储存
  2. bzoj 2152 聪明可可
  3. 深入理解 CAP 定理
  4. Java实现Google第三方登录
  5. centos7磁盘逻辑分区命令_Centos7 磁盘分区概念
  6. Linux笔记-手动配置yum及光盘安装rz
  7. 10月15日 第一天申请成功
  8. linux 挂iscisc存储,基于arm的嵌入式linux操作系统的移植研究-通信与信息系统专业论文.docx...
  9. 使用手册|「3D视觉从入门到精通」知识星球
  10. 实战 Nginx 与 PHP(FastCGI)的安装、配置与优化
  11. 维宏控制卡win7 驱动_雕刻机专用维宏5.55运动驱动控制卡
  12. ITEXT7 实现 PDF文档的合并与拆分
  13. oracle mysql 同义词_Oracle数据库同义词
  14. Hive系列(三)实操
  15. Alist+RaiDrive 给电脑整个80亿GB硬盘
  16. python自然语言处理-广度优先搜索
  17. 三分钟看懂ERP、MES、APS系统的关联和区别
  18. 仅逗oier们一笑(不定期更新中)
  19. 移远串口调试软件QCOM下载
  20. 广东省重点农业龙头企业补贴奖励金及申报条件,补贴50万

热门文章

  1. 三坐标坐标系建立的正确性
  2. 怎样做好产品网络推广销售
  3. [Swift]LeetCode935. 骑士拨号器 | Knight Dialer
  4. 二进制转十进制 8421运算法
  5. 运行游戏时出现0xc000007b错误的解决方法
  6. Calendar类常用api
  7. 金融业-xirr函数------Java实现
  8. 安利向(转):几款免费DDOS攻击测试软件推荐.
  9. 理解HTTP和TCP
  10. 查询数据 抓取 网站数据_有了数据,我就学会了如何在几个小时内抓取网站,您也可以...