Android深入探究笔记之十 -- 使用 ContentProvider 共享数据(二),访问与添加通讯录...

实际应用,“通讯录” 数据读取、添加联系人信息

**查看通讯录数据库:

首先启动模拟器、再打开“File Explorer” 视图,

依次展开:data -- data -- com.android.providers.contacts(注意和 com.android.contacts 区分) -- databases,如图:


点击右上角的导出图标,如图:

将数据库文件导出到电脑磁盘上。

然后,你需要拥有一个能打开 SQLite 数据库的工具。例如我用的 SQLite Expert Personal。可以在网上搜索下载。

打开数据库后,就比较纠结了,需要观察表结构。如果确实看得比较纠结,干脆甩开,以免打击学习激情。

或者一狠心,自己写一个管理联系人的程序,弄个自己的通讯录数据库。

raw_contacts 表: 联系人 ID

data表 : 联系人的数据表。通过raw_contacts_id 与 raw_contacts 表联系

存放的联系人信息如:手机号、姓名、Email 等。

联系人的每一项数据,都会在 data 表产生一条记录。例如,记录手机号的是单独一条记录,记录 Email 的是单独一条记录。并且都用 data1 字段来存放。

也就是说,联系人的所有重要信息,都以单独记录的方式,保存在这张表里的data1字段。用raw_contacts_id 来描述信息所属的联系人。用mimitype_id 来描述 data1 字段存储的数据类型(是 Email 数据么?手机号数据么?座机数据么?)。

假如data1要存放的数据是 “由几个数据组合起来的” ,例如:姓名中的FirstName 和 LastName。

那么就往后存到 data2、data3 里面。组合起来的完整姓名存放在 data1

区分这条数据到底是短信数据、电话数据、Emai等,则是依靠 mimitype_id 字段来区分

mimitype_id 其实是 mimitypes 的外键。观察此表得知:1 表示 email、6 表示姓名、5 表示电话号码。

data2:也用来说明 data1。例如,如果是此记录记录的数据是电话号码,那么若是住宅电话,此字段为1;手机号码为2;单位电话为3......

这些字段的意义很重要,建议读者花10分钟大致看一眼(强烈建议不要花太多时间去研究这个,意义不大),再结合后面的程序,相信会很容易理解。

既然知道了数据存放的方式了,知道它们的字段的名称了,表名也知道了。(当然,编程时我们尽量使用Android提供的常量。否则也许今天写的程序,用到了某某字段,睡一觉起来Android升完级,不认了。)

好了,sql 语句的几个要素都成立了。要存取联系人好了做吧。

当然啦,这个过程要通过 “联系人” 对外提供的接口来完成,毕竟这个程序是人家的,这个表也是人家的,我们不能直接访问第三方程序的数据库。要提供这个接口的方式可多了,ContentProvider 无疑是最佳的选择。

说穿了,其实就是我们在我们的程序中,组拼出我们需要的 sql 语句,通过 ContentProvider 通信机制,将 sql 语句送到 “联系人”程序去执行而已。并不神奇和复杂,不是么?


calls表:存放的呼叫记录。在《Android--删除某联系人的通话记录》中需要操作它

源码在:

${对应版本SDK源码目录}/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java

可以通过查看源码,获知 Uri

...

matcher.addURI(ContactsContract.AUTHORITY,"data", DATA);

matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);

matcher.addURI(ContactsContract.AUTHORITY,"data/phones", PHONES);

matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#",PHONES_ID);

matcher.addURI(ContactsContract.AUTHORITY,"data/phones/filter", PHONES_FILTER);

matcher.addURI(ContactsContract.AUTHORITY,"data/phones/filter/*", PHONES_FILTER);

matcher.addURI(ContactsContract.AUTHORITY, "data/emails",EMAILS);

matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#",EMAILS_ID);

matcher.addURI(ContactsContract.AUTHORITY,"data/emails/lookup/*", EMAILS_LOOKUP);

matcher.addURI(ContactsContract.AUTHORITY,"data/emails/filter", EMAILS_FILTER);

matcher.addURI(ContactsContract.AUTHORITY,"data/emails/filter/*", EMAILS_FILTER);

matcher.addURI(ContactsContract.AUTHORITY,"data/postals", POSTALS);

matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#",POSTALS_ID);

...

要按照电话号码获取某一个联系人可以使用:

Uri uri =Uri.parse("content://com.android.contacts/data/phones/filter/151016899999");

** 示例代码:(上代码之前,最好的建议是:边看这个例子,边打开数据库和源码瞅瞅,对 ContentProvider 的理解会深入很多)

1.AndroidManifest.xml 加入权限

<uses-permission android:name="android.permission.READ_CONTACTS" />

<uses-permission android:name="android.permission.WRITE_CONTACTS" />

2.

  1. packagewjh.android.contact;
  2. importjava.util.ArrayList;
  3. importandroid.content.ContentProviderOperation;
  4. importandroid.content.ContentProviderResult;
  5. importandroid.content.ContentResolver;
  6. importandroid.content.ContentUris;
  7. importandroid.content.ContentValues;
  8. importandroid.database.Cursor;
  9. importandroid.net.Uri;
  10. importandroid.provider.ContactsContract;
  11. importandroid.provider.ContactsContract.RawContacts;
  12. importandroid.provider.ContactsContract.CommonDataKinds.Email;
  13. importandroid.provider.ContactsContract.CommonDataKinds.Phone;
  14. importandroid.provider.ContactsContract.CommonDataKinds.StructuredName;
  15. importandroid.provider.ContactsContract.Contacts.Data;
  16. importandroid.test.AndroidTestCase;
  17. importandroid.util.Log;
  18. /**
  19. *通讯录操作示例
  20. *
  21. */
  22. publicclassContactTestextendsAndroidTestCase{
  23. privatestaticfinalStringTAG="ContactTest";
  24. /**
  25. *获取通讯录中所有的联系人
  26. */
  27. publicvoidtestGetContacts()throwsThrowable{
  28. ContentResolvercontentResolver=this.getContext().getContentResolver();
  29. StringuriStr="content://com.android.contacts/contacts";
  30. Uriuri=Uri.parse(uriStr);
  31. Cursorcursor=contentResolver.query(uri,null,null,null,null);
  32. //遍历联系人
  33. while(cursor.moveToNext()){
  34. //联系人ID
  35. intcontactId=cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));
  36. //联系人显示名称
  37. Stringname=cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
  38. //联系人电话号码需要对另一个表进行查询,所以用到另一个uri:content://com.android.contacts/data/phones
  39. Cursorphones=getContext().getContentResolver().query(
  40. ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
  41. null,
  42. ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"="//根据上一步获取的联系人id来查询
  43. +contactId,null,null);
  44. Stringphone="";
  45. while(phones.moveToNext()){
  46. //"data1"字段,所以此处也可以直接写"data1",但不推荐
  47. phone=phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
  48. }
  49. //再查询Email。uri为:content://com.android.contacts/data/emails
  50. Cursoremails=getContext().getContentResolver().query(
  51. ContactsContract.CommonDataKinds.Email.CONTENT_URI,
  52. null,
  53. ContactsContract.CommonDataKinds.Email.CONTACT_ID+"="
  54. +contactId,null,null);
  55. while(emails.moveToNext()){
  56. /*一样是"data1"字段。现在明白了吧?一个联系人的信息,其实被分成了好几条记录来保存,data1分别保存了各种重要的信息。
  57. *是时候参照打开数据库我前面所说,去瞄一眼它的表结构了!
  58. */
  59. StringemailAddress=emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
  60. Log.i("RongActivity","emailAddress="+emailAddress);
  61. }
  62. emails.close();
  63. Log.i(TAG,"Contact[contactId="+contactId+"name="+name+",phone="+phone+"]");
  64. }
  65. }
  66. /**
  67. *首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
  68. *这时后面插入data表的依据,只有执行空值插入,才能使插入的联系人在通讯录里面可见
  69. */
  70. publicvoidtestInsert(){
  71. ContentValuesvalues=newContentValues();
  72. //首先向RawContacts.CONTENT_URI执行一个空值插入(raw_contacts表),为了获取生成的联系人ID
  73. UrirawContactUri=this.getContext().getContentResolver().insert(RawContacts.CONTENT_URI,values);
  74. //然后获取系统返回的rawContactId,就是新加入的这个联系人的ID
  75. longrawContactId=ContentUris.parseId(rawContactUri);
  76. /*Andorid中,将联系人的姓名、电话、Email
  77. *分别存放在data表的同一个字段的三条记录当中
  78. *因此要Insert三次*/
  79. //往data表入姓名数据
  80. values.clear();
  81. //raw_contacts_id字段,是raw_contacts表id的外键,用于说明此记录属于哪一个联系人
  82. values.put(Data.RAW_CONTACT_ID,rawContactId);
  83. //mimitype_id字段,用于描述此数据的类型,电话号码?Email?....
  84. values.put(Data.MIMETYPE,StructuredName.CONTENT_ITEM_TYPE);//注意查看第二个参数的常量值
  85. values.put(StructuredName.GIVEN_NAME,"文白菜");//这个名字真好听
  86. this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI,values);
  87. //往data表入电话数据
  88. values.clear();
  89. values.put(Data.RAW_CONTACT_ID,rawContactId);
  90. values.put(Data.MIMETYPE,Phone.CONTENT_ITEM_TYPE);
  91. values.put(Phone.NUMBER,"15101689230");
  92. values.put(Phone.TYPE,Phone.TYPE_MOBILE);
  93. this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI,values);
  94. //往data表入Email数据
  95. values.clear();
  96. values.put(Data.RAW_CONTACT_ID,rawContactId);
  97. values.put(Data.MIMETYPE,Email.CONTENT_ITEM_TYPE);
  98. values.put(Email.DATA,"wenlin56@sina.com");
  99. values.put(Email.TYPE,Email.TYPE_WORK);
  100. this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI,values);
  101. }
  102. /**
  103. *在同一个事务当中保存联系人
  104. */
  105. publicvoidtestSave()throwsThrowable{
  106. //文档位置:reference/android/provider/ContactsContract.RawContacts.html
  107. ArrayList<ContentProviderOperation>ops=newArrayList<ContentProviderOperation>();
  108. intrawContactInsertIndex=ops.size();
  109. ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
  110. .withValue(RawContacts.ACCOUNT_TYPE,null)
  111. .withValue(RawContacts.ACCOUNT_NAME,null)
  112. .build());
  113. //文档位置:reference/android/provider/ContactsContract.Data.html
  114. ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
  115. .withValueBackReference(Data.RAW_CONTACT_ID,rawContactInsertIndex)
  116. .withValue(Data.MIMETYPE,StructuredName.CONTENT_ITEM_TYPE)
  117. .withValue(StructuredName.GIVEN_NAME,"文萝卜")
  118. .build());
  119. //更新手机号码:Data.RAW_CONTACT_ID获取上一条语句插入联系人时产生的ID
  120. ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
  121. .withValueBackReference(Data.RAW_CONTACT_ID,rawContactInsertIndex)
  122. .withValue(Data.MIMETYPE,Phone.CONTENT_ITEM_TYPE)
  123. .withValue(Phone.NUMBER,"15101689231")//"data1"
  124. .withValue(Phone.TYPE,Phone.TYPE_MOBILE)
  125. .withValue(Phone.LABEL,"手机号")
  126. .build());
  127. ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
  128. .withValueBackReference(Data.RAW_CONTACT_ID,rawContactInsertIndex)
  129. .withValue(Data.MIMETYPE,Email.CONTENT_ITEM_TYPE)
  130. .withValue(Email.DATA,"wenlin56@yahoo.cn")
  131. .withValue(Email.TYPE,Email.TYPE_WORK)
  132. .build());
  133. //批量插入--在同一个事务当中
  134. ContentProviderResult[]results=this.getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY,ops);
  135. for(ContentProviderResultresult:results){
  136. Log.i(TAG,result.uri.toString());
  137. }
  138. }
  139. }
package wjh.android.contact;import java.util.ArrayList;import android.content.ContentProviderOperation;import android.content.ContentProviderResult;import android.content.ContentResolver;import android.content.ContentUris;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.provider.ContactsContract;import android.provider.ContactsContract.RawContacts;import android.provider.ContactsContract.CommonDataKinds.Email;import android.provider.ContactsContract.CommonDataKinds.Phone;import android.provider.ContactsContract.CommonDataKinds.StructuredName;import android.provider.ContactsContract.Contacts.Data;import android.test.AndroidTestCase;import android.util.Log;/** * 通讯录操作示例 * */public class ContactTest extends AndroidTestCase { private static final String TAG = "ContactTest"; /** * 获取通讯录中所有的联系人 */ public void testGetContacts() throws Throwable { ContentResolver contentResolver = this.getContext().getContentResolver(); String uriStr = "content://com.android.contacts/contacts"; Uri uri = Uri.parse(uriStr); Cursor cursor = contentResolver.query(uri, null, null, null, null); // 遍历联系人 while (cursor.moveToNext()) { // 联系人 ID int contactId = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID)); // 联系人显示名称 String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); // 联系人电话号码需要对另一个表进行查询,所以用到另一个 uri:content://com.android.contacts/data/phones Cursor phones = getContext().getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " // 根据上一步获取的联系人 id 来查询 + contactId, null, null); String phone = ""; while (phones.moveToNext()) { // "data1"字段,所以此处也可以直接写 "data1",但不推荐 phone = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); } // 再查询 Email。uri 为 : content://com.android.contacts/data/emails Cursor emails = getContext().getContentResolver().query( ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId, null, null); while (emails.moveToNext()) { /* 一样是 "data1" 字段。现在明白了吧?一个联系人的信息,其实被分成了好几条记录来保存,data1分别保存了各种重要的信息。 * 是时候参照打开数据库我前面所说,去瞄一眼它的表结构了! */ String emailAddress = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)); Log.i("RongActivity", "emailAddress=" + emailAddress); } emails.close(); Log.i(TAG, "Contact [contactId= "+ contactId +"name=" + name + ", phone=" + phone + "]"); } } /** * 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId * 这时后面插入data表的依据,只有执行空值插入,才能使插入的联系人在通讯录里面可见 */ public void testInsert() { ContentValues values = new ContentValues(); //首先向RawContacts.CONTENT_URI执行一个空值插入(raw_contacts 表), 为了获取生成的联系人 ID Uri rawContactUri = this.getContext().getContentResolver().insert(RawContacts.CONTENT_URI, values); //然后获取系统返回的rawContactId , 就是新加入的这个联系人的 ID long rawContactId = ContentUris.parseId(rawContactUri); /* Andorid 中,将联系人的姓名、电话、Email * 分别存放在 data 表的同一个字段的三条记录当中 * 因此要 Insert 三次 */ //往data表入姓名数据 values.clear(); // raw_contacts_id 字段,是 raw_contacts表id 的外键,用于说明此记录属于哪一个联系人 values.put(Data.RAW_CONTACT_ID, rawContactId); // mimitype_id 字段,用于描述此数据的类型,电话号码?Email?.... values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); // 注意查看第二个参数的常量值 values.put(StructuredName.GIVEN_NAME, "文白菜"); // 这个名字真好听 this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values); //往data表入电话数据 values.clear(); values.put(Data.RAW_CONTACT_ID, rawContactId); values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); values.put(Phone.NUMBER, "15101689230"); values.put(Phone.TYPE, Phone.TYPE_MOBILE); this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values); //往data表入Email数据 values.clear(); values.put(Data.RAW_CONTACT_ID, rawContactId); values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); values.put(Email.DATA, "wenlin56@sina.com"); values.put(Email.TYPE, Email.TYPE_WORK); this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values); } /** * 在同一个事务当中保存联系人 */ public void testSave() throws Throwable{ //文档位置:reference/android/provider/ContactsContract.RawContacts.html ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); int rawContactInsertIndex = ops.size(); ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) .withValue(RawContacts.ACCOUNT_TYPE, null) .withValue(RawContacts.ACCOUNT_NAME, null) .build()); //文档位置:reference/android/provider/ContactsContract.Data.html ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex) .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) .withValue(StructuredName.GIVEN_NAME, "文萝卜") .build()); // 更新手机号码:Data.RAW_CONTACT_ID 获取上一条语句插入联系人时产生的 ID ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex) .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) .withValue(Phone.NUMBER, "15101689231") // "data1" .withValue(Phone.TYPE, Phone.TYPE_MOBILE) .withValue(Phone.LABEL, "手机号") .build()); ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex) .withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE) .withValue(Email.DATA, "wenlin56@yahoo.cn") .withValue(Email.TYPE, Email.TYPE_WORK) .build()); // 批量插入 -- 在同一个事务当中 ContentProviderResult[] results = this.getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); for(ContentProviderResult result : results){ Log.i(TAG, result.uri.toString()); } }}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值