|
通信录的相关数据也保存在数据库文件中,保存的路径为:/data/data/com.android.providers.contacts/databases/contacts2.db。我们在通讯录界面上添加联系人时,内部实际上是通过ContentProvider向数据库中添加记录。通讯录的界面应用与ContentProvider应用是分开的。通讯录界面应用的保存位置为:/data/data/com.android.contacts。
常用的几张表如下:
表raw_contacts:存放联系人的ID
字段display_name:存放姓+名的组合,便于快速得到用户的姓名。注意,当向该表添加联系人时该字段是为null的,只有在向data表中添加姓名时,才会发出update语句来更新该字段。
表data:存放联系人的详细信息,如姓名、手机等,主要几个字段的含义如下:
字段data1:存放具体数据
字段data2:对于电话号码,存放类型:家庭电话、手机号等,2代表手机号
对于邮箱,存放类型
对于姓名,存放名字部分,data3存放姓氏部分
字段mimetype_id:区分数据的类型,5-电话数据,6-姓名数据,1-email数据,对应表mimetypes中的记录ID
这两个表的对应关系是:raw_contacts 1:N data
表mimetypes:数据的类型,如下图:

表calls:呼叫记录
使用ContentResolver对通信录中的数据进行添加、删除、修改和查询操作,需要加入读写联系人信息的权限
<uses-permissionandroid:name="android.permission.READ_CONTACTS" />
<uses-permissionandroid:name="android.permission.WRITE_CONTACTS" />
添加与查询如下:
加入读取联系人信息的权限
<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>
content://com.android.contacts/contacts 操作的数据是联系人信息Uri
content://com.android.contacts/data/phones 联系人电话Uri
content://com.android.contacts/data/emails 联系人Email Uri
ContentProvider的名字可以在源码的文件ContactsProvider\AndroidManifest.xml中找到。如下所示,定义了两个标识,分别为:contacts 或com.android.contacts,如果需要查询具体的业务路径,需要查看类ContactsProvider2中提供的功能,可以通过查找“UriMatcher”来找到。
<provider android:name="ContactsProvider2"
android:authorities="contacts;com.android.contacts"
android:label="@string/provider_label"
android:multiprocess="false"
android:readPermission="android.permission.READ_CONTACTS"
android:writePermission="android.permission.WRITE_CONTACTS">
<path-permission
android:pathPrefix="/search_suggest_query"
android:readPermission="android.permission.GLOBAL_SEARCH"/>
<path-permission
android:pathPrefix="/search_suggest_shortcut"
android:readPermission="android.permission.GLOBAL_SEARCH"/>
<path-permission
android:pathPattern="/contacts/.*/photo"
android:readPermission="android.permission.GLOBAL_SEARCH"/>
<grant-uri-permission android:pathPattern=".*" />
</provider>
ContactsProvider2中提供的部分业务路径:
static{
// Contacts URI matching table
final UriMatcher matcher = sUriMatcher;
matcher.addURI(ContactsContract.AUTHORITY, "contacts",CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#",CONTACTS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data",CONTACTS_ID_DATA);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/#/entities", CONTACTS_ID_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/#/suggestions",
AGGREGATION_SUGGESTIONS);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/#/suggestions/*",
AGGREGATION_SUGGESTIONS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo",CONTACTS_ID_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/#/display_photo",
CONTACTS_ID_DISPLAY_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/#/stream_items",
CONTACTS_ID_STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter",CONTACTS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/filter/*", CONTACTS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*",CONTACTS_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/photo",
CONTACTS_LOOKUP_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/#/data",
CONTACTS_LOOKUP_ID_DATA);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/#/photo",
CONTACTS_LOOKUP_ID_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/display_photo",
CONTACTS_LOOKUP_DISPLAY_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/#/display_photo",
CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/entities",
CONTACTS_LOOKUP_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/#/entities",
CONTACTS_LOOKUP_ID_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/stream_items",
CONTACTS_LOOKUP_STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/lookup/*/#/stream_items",
CONTACTS_LOOKUP_ID_STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/as_vcard/*", CONTACTS_AS_VCARD);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
CONTACTS_AS_MULTI_VCARD);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/strequent/", CONTACTS_STREQUENT);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/strequent/filter/*",
CONTACTS_STREQUENT_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*",CONTACTS_GROUP);
matcher.addURI(ContactsContract.AUTHORITY,"contacts/frequent", CONTACTS_FREQUENT);
… …
}
读取联系人信息
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
StringcontactId =cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
Stringname = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Cursorphones =getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+contactId,
null, null);
while (phones.moveToNext()) {
String phoneNumber = phones.getString(phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.i("RongActivity", "phoneNumber="+phoneNumber);
}
phones.close();
Cursor emails =getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " +contactId,
null, null);
while (emails.moveToNext()) {
// This would allow you get several email addresses
String emailAddress =emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
Log.i("RongActivity", "emailAddress="+ emailAddress);
}
emails.close();
}
cursor.close();
==================== 添加联系人===========================
方法一:
/**
* 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
* 这时后面插入data表的依据,只有执行空值插入,才能使插入的联系人在通讯录里面可见
*/
publicvoid testInsert() {
ContentValuesvalues = new ContentValues();
//首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
UrirawContactUri =this.getContext().getContentResolver().insert(RawContacts.CONTENT_URI, values);
longrawContactId = ContentUris.parseId(rawContactUri);
//往data表入姓名数据
values.clear();
values.put(Data.RAW_CONTACT_ID,rawContactId);
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,"13921009789");
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,"liming@itcast.cn");
values.put(Email.TYPE,Email.TYPE_WORK);
this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI,values);
}
方法二:批量添加,处于同一个事务中
publicvoid testSave() throws Throwable{
//文档位置:reference\android\provider\ContactsContract.RawContacts.html
ArrayList<ContentProviderOperation>ops = new ArrayList<ContentProviderOperation>();
intrawContactInsertIndex = 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());
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,"13671323809")
.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,"liming@itcast.cn")
.withValue(Email.TYPE,Email.TYPE_WORK)
.build());
ContentProviderResult[]results = this.getContext().getContentResolver()
.applyBatch(ContactsContract.AUTHORITY,ops);
for(ContentProviderResultresult : results){
Log.i(TAG,result.uri.toString());
}
}
下面创建应用contacts,ApplicationName:访问通讯录,Package Name:cn.itcast.contacts,Create Activity:MainActivity。在该项目中通过单元测试来获取联系人信息。
1. 配置单元测试的环境/contacts/AndroidManifest.xml
<?xml version="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="cn.itcast.contacts"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<uses-library android:name="android.test.runner"/>
</application>
<uses-sdk android:minSdkVersion="8" />
<instrumentationandroid:targetPackage="cn.itcast.contacts"android:name="android.test.InstrumentationTestRunner" />
<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>
<uses-permissionandroid:name="android.permission.WRITE_CONTACTS"/>
</manifest>
所需权限可以在源码中的AndroidManifest.xml中查找到。
2. 创建测试类ContactsTest
package cn.itcast.test;
import java.util.ArrayList;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.util.Log;
public class ContactsTest extendsAndroidTestCase {
privatestatic final String TAG = "ContactsTest";
//获取所有联系人
publicvoid testContacts()throws Exception{
Uriuri = Uri.parse("content://com.android.contacts/contacts");
ContentResolverresolver = getContext().getContentResolver();
//查询raw_contacts._id
Cursorcursor = resolver.query(uri, new String[]{"_id"}, null, null, null);
while(cursor.moveToNext()){
intcontactid = cursor.getInt(0);
StringBuildersb = new StringBuilder("contactid=");
sb.append(contactid);
//content://com.android.contacts/contacts/#/data
uri= Uri.parse("content://com.android.contacts/contacts/"+contactid+ "/data");
//查询data表中相关的字段
Cursordatacursor = resolver.query(uri, newString[]{"mimetype","data1","data2"}, null, null,null);
while(datacursor.moveToNext()){
Stringdata = datacursor.getString(datacursor.getColumnIndex("data1"));
Stringtype = datacursor.getString(datacursor.getColumnIndex("mimetype"));
if("vnd.android.cursor.item/name".equals(type)){//姓名
sb.append(",name="+data);
}elseif("vnd.android.cursor.item/email_v2".equals(type)){//email
sb.append(",email="+data);
}elseif("vnd.android.cursor.item/phone_v2".equals(type)){//phone
sb.append(",phone="+data);
}
}
Log.i(TAG,sb.toString());
}
}
//根据号码获取联系人的姓名
publicvoid testContactNameByNumber()throws Exception{
Stringnumber = "18601025011";
Uriuri = Uri.parse("content://com.android.contacts/data/phones/filter/"+number);
ContentResolverresolver = getContext().getContentResolver();
Cursorcursor = resolver.query(uri, newString[]{"display_name"}, null, null, null);
if(cursor.moveToFirst()){
Stringname = cursor.getString(0);
Log.i(TAG,name);
}
cursor.close();
}
//添加联系人。缺点:由多个操作完成,无法保证全部都成功
publicvoid testAddContact()throws Exception{
Uriuri = Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolverresolver = getContext().getContentResolver();
ContentValuesvalues = new ContentValues();
long contactid =ContentUris.parseId(resolver.insert(uri, values));
//添加姓名
uri= Uri.parse("content://com.android.contacts/data");
values.put("raw_contact_id",contactid);
values.put("mimetype","vnd.android.cursor.item/name");
values.put("data2","张小小");
resolver.insert(uri,values);
//添加电话
values.clear();
values.put("raw_contact_id",contactid);
values.put("mimetype","vnd.android.cursor.item/phone_v2");
values.put("data2","2");
values.put("data1","13671323507");
resolver.insert(uri,values);
//添加Email
values.clear();
values.put("raw_contact_id",contactid);
values.put("mimetype","vnd.android.cursor.item/email_v2");
values.put("data2","2");
values.put("data1","zhangxx@csdn.net");
resolver.insert(uri,values);
}
//在同一个事务中完成联系人各项数据的添加
publicvoid testAddContact2()throws Exception{
Uriuri = Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolverresolver = getContext().getContentResolver();
ArrayList<ContentProviderOperation>operations = new ArrayList<ContentProviderOperation>();
ContentProviderOperationop1 = ContentProviderOperation.newInsert(uri)
.withValue("account_name",null) //account_name专门用于存放google的登录帐号
.build();
operations.add(op1);
//注意:因为是在一个事务中完成的,所以不知道raw_contact_id的值,
// 因此在设置raw_contact_id时使用withValueBackReference
// .withValueBackReference("raw_contact_id",0)的含义是使用第一个操作对象添加完成后所返回的记录ID作为字段的值
uri= Uri.parse("content://com.android.contacts/data");
ContentProviderOperationop2 = ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id",0)
.withValue("mimetype","vnd.android.cursor.item/name")
.withValue("data2","李小龙")
.build();
operations.add(op2);
ContentProviderOperationop3 = ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id",0)
.withValue("mimetype","vnd.android.cursor.item/phone_v2")
.withValue("data1","13560650505")
.withValue("data2","2")
.build();
operations.add(op3);
ContentProviderOperationop4 = ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id",0)
.withValue("mimetype","vnd.android.cursor.item/email_v2")
.withValue("data1","liming@sohu.com")
.withValue("data2","2")
.build();
operations.add(op4);
resolver.applyBatch("com.android.contacts",operations);
}
}
|