直接看代码,有详细注释。
1.联系人的数据库文件的位置
/data/data/com.android.providers.contacts/databases.contacts2.db
/data/data/com.android.providers.contacts/databases.contacts2.db
2.数据库中重要的几张表
contacts表:该表保存了所有的手机联系人,每个联系人占一行,该表保存了联系人的ContactID、联系次数、 最后一次联系的时间、是否含有号码、是否被添加到收藏夹等信息。可以与表的字段名相对应 理解。
raw_contacts表:该表保存了所有创建过的手机联系人,每个联系人占一行,表里有一列标识该联系人是否 被删除,该表保存了两个ID:RawContactID和ContactID,从而将contacts表和raw_contacts表 联系起来。该表保存了联系人的RawContactID、ContactID、联系次数、最后一次联系的时间 是否被添加到收藏夹、显示的名字、用于排序的汉语拼音等信息。
mimetypes 表:该表定义了所有的MimeTypeID,即联系人的各个字段的唯一标志。
data表:该表保存了所有创建过的手机测联系人的所有信息,每个字段占一行 ,该表保存了两个ID:MimeTypeI D和RawContactID,从而将data表和raw_contacts表联系起来。联系人的所有信息保存在列data1至data 15中,各列中保存的内容根据MimeTypeID的不同而不同。如保存号码(MimeTypeID=5)的那行数据中 ,data1列保存号码,data2列保存号码类型(手机号码/家庭号码/工作号码等)。
package com.example.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 extends AndroidTestCase {
private static final String tag="ContactsTest";
public void testContacts() throws Exception //测试查询联系人
{
/*
Content URI 是一种用于标识 Provider 数据的 URI。 Content URI 包括了整个 Provider
的符号名称(authority)和表名(path)。 调用客户端的方法访问 Provider 数据表时,
表的 Content URI 是参数之一。Uri uri=Uri.parse("content://com.android.contacts/contacts");
*/
//这里的字符串com.android.contacts 是 Provider 的 authority 部分, 字符串 contacts 是数据表的 path 部分。 字符串 content:// (scheme)是必须指定的,以表明这是一个 Content URI。
Uri uri=Uri.parse("content://com.android.contacts/contacts");
ContentResolver resolver=this.getContext().getContentResolver();
Cursor cursor=resolver.query(uri, new String[]{"_id"}, null, null, null);
while(cursor.moveToNext())
{
int contactid=cursor.getInt(0);
StringBuffer sb=new StringBuffer("contactid=");
sb.append(contactid);
uri=Uri.parse("content://com.android.contacts/contacts/"+contactid+"/data");
/*
Cursor = getContentResolver().query(
uri, // 联系人的URI
mProjection, // 需要返回的列
mSelectionClause, // 查询条件
mSelectionArgs, // 查询条件的参数
mSortOrder); // 返回结果的排序要求
*/
Cursor datacursor=resolver.query(uri //联系人的URI
, new String[]{"mimetype","data1","data2"}//需要返回的列
, null //查询条件
, null //查询条件的参数
, null); //返回结果的排序要求
while(datacursor.moveToNext())
{
String data=datacursor.getString(datacursor.getColumnIndex("data1"));
String type=datacursor.getString(datacursor.getColumnIndex("mimetype"));
if("vnd.android.cursor.item/name".equals(type))
{
sb.append(",name="+data);
}
else if("vnd.android.cursor.item/email_v2".equals(type))
{
sb.append(",email="+data);
}else if("vnd.android.cursor.item/phone_v2".equals(type)){
sb.append(",phone="+data);
}
}
Log.i(tag, sb.toString());
}
cursor.close();
}
public void testContactsNameByNumer() throws Exception
{
String number="15241499053";
//Provider 提供了对单条记录的访问能力,只要在 URI 后面跟一个 ID 值即可。 例如,要根据电话找到联系人,只需要在后面加上number,可以使用以下 Content URI:
Uri uri=Uri.parse("content://com.android.contacts/data/phones/filter/"+number);
ContentResolver resolver=this.getContext().getContentResolver();
Cursor cursor=resolver.query(uri, new String[]{"display_name"}, null, null, null);
while(cursor.moveToNext())
{
String name=cursor.getString(0);
Log.i(tag,name);
}
cursor.close();
}
/*
调用 ContentResolver.insert() 方法可以将数据插入 Provider 到中去。 该方法将在 Provider 中插入新数据行,并返回一个指向改行数据的 Content URI。 以下代码将在 com.android.contacts Provider 中插入一条新的联系人:
新行的数据存放在一个 ContentValues 对象中, 对象中,这个对象类似于只包含一条数据的游标。 该对象中的各个字段的类型可以各不相同。如果不需要指定值,可以用 ContentValues.putNull() 方法置为 null。
上述代码并没有给 _ID 字段赋值,因为这个字段是由系统自动维护的。 Provider 会自动给插入行的 _ID 字段赋一个唯一值,并且通常把它作为表的主键使用。
*/
public void testAddContacts() throws Exception
{
Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver=getContext().getContentResolver();
ContentValues values=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.put("raw_contact_id", contactid);
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
values.put("data2", "2");
values.put("data1", "4399101");
resolver.insert(uri, values);
//Email
values.put("raw_contact_id", contactid);
values.put("mimetype", "vnd.android.cursor.item/email_v2");
values.put("data2", "2");
values.put("data1", "43243454@qq.com");
resolver.insert(uri, values);
}
/*
在开发应用时,访问 Provider 还有其他三种重要的形式:
批量访问:通过 ContentProviderOperation 类的一些方法,可以创建批量访问任务,并通过 ContentResolver.applyBatch() 来提交。
异步查询:在单独的线程中执行查询。有一种方案是用 CursorLoader 对象来实现。在指南 Loaders 中给出了示例。
利用 Intent 访问数据: 虽然不能向 Provider 直接发送 Intent,但可以向 Provider 所在应用发送 Intent, 通常这些应用都具备修改 Provider 数据的能力。
下面这种就是以通过 ContentProviderOperation进行的批量添加。
*/
public void testAddContact2() throws Exception{
Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver=getContext().getContentResolver();
//创建一个 ContentProviderOperation 对象的数组,并通过 ContentResolver.applyBatch() 方法将它传给 Content Provider。
ArrayList<ContentProviderOperation> operations=new ArrayList<ContentProviderOperation>();
ContentProviderOperation op1=ContentProviderOperation.newInsert(uri)
.withValue("account_name", null)
.build();
operations.add(op1);
/*
* 在调用时不是指定某个 Content URI,而是要给出 Content Provider 的 authority。
* 数组中的每个 ContentProviderOperation 对象可以对不同的数据表进行操作。
* ContentResolver.applyBatch() 返回的结果也是数组。
*/
uri=Uri.parse("ontent://com.android.contacts/data");
ContentProviderOperation op2=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/name")
.withValue("data2", "李小龙")
.build();
operations.add(op2);
ContentProviderOperation op3=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/phone_v2")
.withValue("data1", "110119")
.withValue("data2", "2")
.build();
operations.add(op3);
ContentProviderOperation op4=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/email_v2")
.withValue("data1", "lixiaolong@sohu.com")
.withValue("data2", "2")
.build();
operations.add(op4);
resolver.applyBatch("com.android.contacts", operations);
}
}
完成以上测试代码还需要添加对联系人表操作的响应权限。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.contacts"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<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>
<instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.example.contacts"></instrumentation>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>"
</manifest>
这是对联系人操作对应的权限:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"
/>
这是对测试单元对应的权限和依赖库:
<instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.example.contacts"></instrumentation>
<uses-library android:name="android.test.runner"/>