Android手机通讯录解析(转)
1 、通讯录应用介绍
通讯录应用是 Android 自带的应用程序,我们看到此应用的时候,可能只认为这是一个应用,用数据库存储数据,但是实际上不是这样的。
通讯录是
ContentProvider
的应用,通讯录由两部分组成:
(1)com.android.providers.contacts
的
ContentProvider
:真正存储数据的
ContentProvider
(2)com.android.contacts
:运用
ContentResolver
获取数据的图形用户界面;
2 、通讯录数据库目录结构
( 1 )、通讯录数据库目录结构的获取
打开 Eclipse 软件,调出虚拟机。如果已经创建过虚拟机,则只需要运行程序,如下图
如果没有创建就需要再去创建一个虚拟机。
调出虚拟机效果如下:
接下来就要:
通讯录是存放在 /data/data/com.android.providers.contacts/databases/contacts2.db 里的
接下来就是导出联系人的数据库:
导出后得到这样的数据库:
这个是 QLite Database Browser 工具
打开它把 contacts2.db 数据库拖进去就可以了。
得到如下:
查看数据库表中数据:
到此如何获取手机数据到此结束,前期工作完成。
3 、通讯录数据库结构介绍
这是手机通讯录直接相关联的几张表,圈出来的比较重要。
( 1 ) mimetype 表
( 2 ) contacts 表
此处的 name_raw_contact_id 为 raw_contacts 表的主键
( 3 ) raw_contacts 表
此处 _id 为 raw_contacts 表的主键, version 翻译后叫版本号 , 一般一个联系人创建后版本号就从 2 开始,当这个联系人每做一次操作后 version 都增加,一般是增加 1 ,但有时不确定反正他会增加。对于 Android 手机而言,它本身自带了一个通讯录,他对与联系人的删除操作就用户而言是删除了,因为在用户界面没有了该来联系人了。但是事实上 Android 并没有真正删除它。仅仅是在 raw_contacts 表中把 deleted 字段标记为了 1 ,意思是说 “1” 代表删除了, “0” 代表联系人没删除,是存在的。 contact_id 来自 contacts 表的主键。这样 contacts 和 raw_contacts 联系在一起了。
( 4 ) data 表
data 表中的 mimetype_id 来自 mimetype 表, raw_contact_id 来自 raw_contacts 表。一般来说 data1 到 data15 的字段都是存放数据的。
其中 mimetype_id=5 代表电话号码 mimetype_id=7 代表姓名。当 mimetype_id=5 时 data1 存放电话号码, data2 存放电话号码的类型, data2=2, 代表电话号码为手机号。
2 、 3 、手机通讯录的操作介绍
( 1 )新增 这里介绍了两种方法
/**
* 将一组联系人添加到手机通讯录中
*
* @param Contacts
* @param context
*/
public static void AddContact(ContactInfo[] Contacts, Context context) {
for ( int i = 0; i < Contacts.length; i++) {
ContentValues values = new ContentValues();
// 下面的操作会根据 RawContacts 表中已有的 rawContactId 使用情况自动生成新联系人的 rawContactId
Uri rawContactUri = context.getContentResolver().insert(
RawContacts. CONTENT_URI , values);
long rawContactId = ContentUris. parseId (rawContactUri);
// 向 data 表插入姓名数据
if (Contacts[i].Name != "") {
values.clear();
values.put(Data. RAW_CONTACT_ID , rawContactId);
values.put(Data. MIMETYPE , StructuredName. CONTENT_ITEM_TYPE );
values.put(StructuredName. GIVEN_NAME , Contacts[i].Name);
context.getContentResolver().insert(
ContactsContract.Data. CONTENT_URI , values);
}
// 向 data 表插入电话数据
if (Contacts[i].Num != "") {
values.clear();
values.put(Data. RAW_CONTACT_ID , rawContactId);
values.put(Data. MIMETYPE , Phone. CONTENT_ITEM_TYPE );
values.put(Phone. NUMBER , Contacts[i].Num);
values.put(Phone. TYPE , Phone. TYPE_MOBILE );
context.getContentResolver().insert(
ContactsContract.Data. CONTENT_URI , values);
}
}
}
/**
* 将单个联系人添加到通讯录中
*
* @param Contact
* @param context
*/
public static void AddContact(ContactInfo Contact, Context context) {
ContentValues values = new ContentValues();
// 下面的操作会根据 RawContacts 表中已有的 rawContactId 使用情况自动生成新联系人的 rawContactId
Uri rawContactUri = context.getContentResolver().insert(
RawContacts. CONTENT_URI , values);
long rawContactId = ContentUris. parseId (rawContactUri);
// 向 data 表插入姓名数据
If (Contact.Name != "") {
values.clear();
values.put(Data. RAW_CONTACT_ID , rawContactId);
values.put(Data. MIMETYPE , StructuredName. CONTENT_ITEM_TYPE );
values.put(StructuredName. GIVEN_NAME , Contact.Name);
context.getContentResolver().insert(
ContactsContract.Data. CONTENT_URI , values);
}
// 向 data 表插入电话数据
if (Contact.Num != "") {
values.clear();
values.put(Data. RAW_CONTACT_ID , rawContactId);
values.put(Data. MIMETYPE , Phone. CONTENT_ITEM_TYPE );
values.put(Phone. NUMBER , Contact.Num);
values.put(Phone. TYPE , Phone. TYPE_MOBILE );
context.getContentResolver().insert(
ContactsContract.Data. CONTENT_URI , values);
}
}
public static void testAddContacts(ContactInfo[] Contacts, Context context){
for ( int i = 0; i < Contacts.length; i++) {
// 插入 raw_contacts 表,并获取 _id 属性
Uri uri = Uri. parse ("content://com.android.contacts/raw_contacts"); // 获取操作的表的路径
ContentResolver resolver = context.getContentResolver(); // 类似 .NET 中的上下文对象或数据库操作
ContentValues values = new ContentValues(); // 定义一个联系人操作变量
long contact_id = ContentUris. parseId (resolver.insert(uri, values));
// 插入 data 表
uri = Uri. parse ("content://com.android.contacts/data");
//add Name
values.put("raw_contact_id", contact_id);
values.put(Data. MIMETYPE ,"vnd.android.cursor.item/name");
values.put("data2", Contacts[i].Name);
values.put("data1", Contacts[i].Name);
resolver.insert(uri, values);
values.clear();
//add Phone
values.put("raw_contact_id", contact_id);
values.put(Data. MIMETYPE ,"vnd.android.cursor.item/phone_v2");
values.put("data2", "2"); // 手机
values.put("data1", Contacts[i].Num);
resolver.insert(uri, values);
values.clear();
}
}
public static void testAddContacts(ContactInfo Contact, Context context){
// 插入 raw_contacts 表,并获取 _id 属性
Uri uri = Uri. parse ("content://com.android.contacts/raw_contacts");
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
long contact_id = ContentUris. parseId (resolver.insert(uri, values));
// 插入 data 表
uri = Uri. parse ("content://com.android.contacts/data");
//add Name
values.put("raw_contact_id", contact_id);
values.put(Data. MIMETYPE ,"vnd.android.cursor.item/name");
values.put("data2", Contact.Name);
values.put("data1", Contact.Name);
resolver.insert(uri, values);
values.clear();
//add Phone
values.put("raw_contact_id", contact_id);
values.put(Data. MIMETYPE ,"vnd.android.cursor.item/phone_v2");
values.put("data2", "2"); // 手机
values.put("data1", Contact.Num);
resolver.insert(uri, values);
values.clear();
/** //add email
values.put("raw_contact_id", contact_id);
values.put(Data.MIMETYPE,"vnd.android.cursor.item/email_v2");
values.put("data2", "2"); // 单位
values.put("data1", "www.2cto.com");
resolver.insert(uri, values); */
}
( 2 )修改 此处也介绍两种方法针对 ID 的来源
/**
* 更新联系人
*
* @param context
* 上下文对象
* @param name
* 联系人姓名
* @param number
* 手机号
* @param ContactId
*ID (此 ID 是由通讯录数据库自动生成的 , 并且该 ID 为 contacts 表中的主键)
*/
public static void testUpdate(Context context, String name,
String number, String ContactId){
String version="0";
String rawContactsId ="";
ContentResolver cr = context.getContentResolver();
// 依 contacts 表中的主键查找 raw_contacts 表中的 ID 即主键
Cursor rawContactsIdCur = cr.query(RawContacts. CONTENT_URI ,
null , RawContacts. CONTACT_ID + " = ?",
new String[] { ContactId }, null );
if (rawContactsIdCur.moveToFirst()) {
version = rawContactsIdCur.getString(rawContactsIdCur
.getColumnIndex(RawContacts. VERSION ));
// 获得 raw_contacts 表中的主键
rawContactsId = rawContactsIdCur.getString(rawContactsIdCur
.getColumnIndex(RawContacts. _ID ));
}
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
Uri uri = Uri. parse ("content://com.android.contacts/data");// 对 data 表的所有数据操作
resolver = context.getContentResolver();
values.clear();
values = new ContentValues();
values.put("data1", number);
// 依 raw_contacts 表中的 ID 对 data 表做相应的修改
resolver.update(uri, values, "mimetype=? and
raw_contact_id=?", new String[]{"vnd.android.cursor.item/phone_v2",rawContactsId}) ;
values.clear();
values.put("data2", name);
values.put("data1", name);
resolver.update(uri, values, "mimetype=? and
raw_contact_id=?", new String[]{"vnd.android.cursor.item/name",rawContactsId}) ;
rawContactsIdCur.close();
}
/**
* 更新联系人
*
* @param context
* 上下文对象
* @param name
* 联系人姓名
* @param number
* 手机号
* @param ContactId
*ID (此 ID 是由通讯录数据库自动生成的 , 并且该 ID 为 raw_contacts 表中的主键)
*/
public static void updateContact(Context context, String name,
String number, String ContactId)
{
ContentValues values = new ContentValues();
// 更新姓名
values.clear();
values.put(StructuredName. GIVEN_NAME , name);
context.getContentResolver().update(ContactsContract.Data. CONTENT_URI ,
values,
Data. RAW_CONTACT_ID + "=? and " + Data. MIMETYPE + "=?",
new String[] { ContactId, StructuredName. CONTENT_ITEM_TYPE });
// 更新电话
values.clear();
values.put(ContactsContract.CommonDataKinds.Phone. NUMBER , number);
context.getContentResolver().update(ContactsContract.Data. CONTENT_URI ,
values,
Data. RAW_CONTACT_ID + "=? and " + Data. MIMETYPE + "=?",
new String[] { ContactId, Phone. CONTENT_ITEM_TYPE });
}
( 3 )删除
这里是彻底删除,把 data,contacts,raw_contacts 表中的数据以并删除,而 Android 自带的手机通讯录删除操作不是真正意义上的删除,仅仅是将 raw_contacts 表中的 deleted 字段改为了 “1” 。
/**
* 删除联系人
*
* @param context
* 上下文对象
* @param ContactId
* 联系人 ID
*ID (此 ID 是请求数据传递过来的 , 并且该 ID 为 raw_contacts 表中的主键)
*/
public static void DeleteContact(Context context, String ContactId)
{
Uri uri = Uri. parse ("content://com.android.contacts/raw_contacts");
ContentResolver resolver = context.getContentResolver();
Cursor cursor =
resolver.query(uri, new String[]{RawContacts. _ID },"contact_id=?", new String[]{ContactId
}, null );
if (cursor.moveToFirst()){
int id = cursor.getInt(0);
// 根据 id 删除 data 中的相应数据
resolver.delete(uri, "_id=?", new String[]{id+""});
uri = Uri. parse ("content://com.android.contacts/data");
resolver.delete(uri, "raw_contact_id=?", new String[]{id+""});
}
}
( 4 )读取联系人
/**
* 获取联系人
*
* @return list 数组,包含手机通讯录中的联系人
*/
private List<Map<String, Object>> getContacts() {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
// 获取用来操作数据的类的对象,对联系人的基本操作都是使用这个对象
ContentResolver cr = getContentResolver();
// 查询 contacts 表的所有记录
Cursor cur = cr.query(ContactsContract.Contacts. CONTENT_URI , null ,
null , null , null );
String deleted = "1";
String version = "0";
// 如果记录不为空
if (cur.getCount() > 0) {
// 游标初始指向查询结果的第一条记录的上方,执行 moveToNext 函数会判断
// 下一条记录是否存在,如果存在,指向下一条记录。否则,返回 false 。
while (cur.moveToNext()) {
// 取得联系人的 ID 索引值
String contactId = cur.getString(cur
.getColumnIndex(ContactsContract.Contacts. _ID ));
if (contactId== null )
{
contactId="No Id";
}
// 取得联系人的名字索引
int nameIndex = cur.getColumnIndex(PhoneLookup. DISPLAY_NAME );
String name = cur.getString(nameIndex);
if (name== null )
{
name="No name";
}
String rawContactsId = "";
String id = cur.getString(cur
.getColumnIndex(ContactsContract.Contacts. _ID ));
// str += "ID:" + id + "\n"; 二、对联系人的基本操作 (4)
// 读取 rawContactsId
Cursor rawContactsIdCur = cr.query(RawContacts. CONTENT_URI ,
null , RawContacts. CONTACT_ID + " = ?",
new String[] { id }, null );
// 该查询结果一般只返回一条记录,所以我们直接让游标指向第一条记录
if (rawContactsIdCur.moveToFirst()) {
// 读取第一条记录的 RawContacts._ID 列的值
rawContactsId = rawContactsIdCur.getString(rawContactsIdCur
.getColumnIndex(RawContacts. _ID ));
deleted = rawContactsIdCur.getString(rawContactsIdCur
.getColumnIndex(RawContacts. DELETED ));
version = rawContactsIdCur.getString(rawContactsIdCur
.getColumnIndex(RawContacts. VERSION ));
}
rawContactsIdCur.close();
// 读取号码
Cursor PhoneCur = cr.query(
ContactsContract.CommonDataKinds.Phone. CONTENT_URI ,
null ,
ContactsContract.CommonDataKinds.Phone. RAW_CONTACT_ID
+ " =?", new String[] { rawContactsId }, null );
// 上面的 ContactsContract.CommonDataKinds.Phone.CONTENT_URI
// 可以用下面的 phoneUri 代替
// Uri
// phoneUri=Uri.parse("content://com.android.contacts/data/phones");
// 二、对联系人的基本操作 (6)
// 一个联系人可能有多个号码,需要遍历
if (!PhoneCur.moveToNext())
{
// 获取电话号码为空的情况即游标未找到的情况
String number="No number";
// // 获取号码类型
Map<String, Object> map = new HashMap<String, Object>();
map.put("phone", number + ",");
map.put("name", name + "|");
map.put("version", version + ",");
map.put("deleted", deleted + ",");
map.put("id", contactId + ",");
list.add(map);
}
else
{
// 号获取码
String number = PhoneCur
.getString(PhoneCur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone. NUMBER ));
// // 获取号码类型
Map<String, Object> map = new HashMap<String, Object>();
map.put("phone", number + ",");
map.put("name", name + "|");
map.put("version", version + ",");
map.put("deleted", deleted + ",");
map.put("id", contactId + ",");
list.add(map);
}
while (PhoneCur.moveToNext()) {
// 号获取码
String number = PhoneCur
.getString(PhoneCur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone. NUMBER ));
if (number== null )
{
number="No number";
}
// // 获取号码类型
Map<String, Object> map = new HashMap<String, Object>();
map.put("phone", number + ",");
map.put("name", name + "|");
map.put("version", version + ",");
map.put("deleted", deleted + ",");
map.put("id", contactId + ",");
list.add(map);
}
PhoneCur.close();// 关闭游标
}
}
return list;
}
}