Android中如何解析特定联系人全量数据查询的Cursor结果
嘿,我懂你这种抓瞎的感觉——官方给的那堆DATA1到DATA15字段确实太抽象了,完全看不出对应啥信息。其实核心逻辑很简单:这些DATA字段的含义完全由Data.MIMETYPE决定,不同类型的联系人数据(比如电话、邮箱、地址)会用不同的MIMETYPE,每个MIMETYPE下的DATA字段都有明确的对应关系。我给你拆解清楚,再给你一套可直接用的解析方案。
核心:MIMETYPE与DATA字段的对应关系
每个联系人的不同信息(姓名、电话、邮箱等)都是Data表中的单独行,每行的MIMETYPE标识了这行数据的类型,对应的DATA字段就有了具体含义。下面是你要的常见类型对应关系:
1. 联系人姓名(Structured Name)
- MIMETYPE:
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE - DATA1: 全名(DisplayName)
- DATA2: 名(Given Name)
- DATA3: 姓(Family Name)
- DATA4: 前缀(Prefix,比如Mr.)
- DATA5: 后缀(Suffix,比如Jr.)
2. 电话号码
- MIMETYPE:
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE - DATA1: 电话号码字符串
- DATA2: 电话类型(比如
Phone.TYPE_MOBILE、Phone.TYPE_HOME,对应整数) - DATA3: 自定义标签(如果用户给电话加了自定义名称,比如“外卖电话”)
3. 邮箱地址
- MIMETYPE:
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE - DATA1: 邮箱地址
- DATA2: 邮箱类型(
Email.TYPE_WORK、Email.TYPE_PERSONAL等) - DATA3: 自定义标签
4. 物理地址
- MIMETYPE:
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE - DATA1: 完整地址字符串
- DATA2: 地址类型(
StructuredPostal.TYPE_HOME、StructuredPostal.TYPE_WORK等) - DATA3: 街道(Street)
- DATA4: PO Box
- DATA5: 城市(City)
- DATA6: 州/省(Region)
- DATA7: 邮政编码(Postal Code)
- DATA8: 国家(Country)
5. 传真号码
- MIMETYPE:
ContactsContract.CommonDataKinds.Fax.CONTENT_ITEM_TYPE - DATA1: 传真号码
- DATA2: 传真类型(
Fax.TYPE_HOME、Fax.TYPE_WORK等) - DATA3: 自定义标签
6. 社交账号(比如Facebook ID)
- MIMETYPE:
ContactsContract.CommonDataKinds.SocialProfile.CONTENT_ITEM_TYPE - DATA1: 社交账号的用户名/ID
- DATA2: 社交平台类型(
SocialProfile.TYPE_FACEBOOK、SocialProfile.TYPE_TWITTER等) - DATA3: 账号显示名称
- DATA4: 社交平台的URL
其他常用类型(可选)
- 备注:
ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE→ DATA1是备注内容 - 网址:
ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE→ DATA1是网址,DATA2是网址类型
先过滤出特定联系人的数据
既然你有Lookup Key和Contact ID,查询的时候一定要加筛选条件,只拉取目标联系人的数据,避免处理全量数据。示例查询Uri和Selection:
// 用Lookup Key筛选(推荐,因为Contact ID可能变化,Lookup Key是永久标识) String selection = ContactsContract.Data.LOOKUP_KEY + " = ?"; String[] selectionArgs = new String[]{yourLookupKey}; // 或者用Contact ID筛选 // String selection = ContactsContract.Data.CONTACT_ID + " = ?"; // String[] selectionArgs = new String[]{yourContactId}; // 执行查询 Cursor cursor = getContentResolver().query( ContactsContract.Data.CONTENT_URI, PROJECTION, // 你定义的那组投影字段 selection, selectionArgs, null );
解析Cursor的示例代码
下面是遍历Cursor,把不同类型的数据整理到一个自定义的Contact对象里的示例(你可以根据需求调整):
// 先定义一个存储联系人信息的类或者HashMap class ContactDetails { String fullName; List<PhoneInfo> phones = new ArrayList<>(); List<EmailInfo> emails = new ArrayList<>(); List<AddressInfo> addresses = new ArrayList<>(); List<SocialProfileInfo> socialProfiles = new ArrayList<>(); // 其他字段:传真、备注等 // 内部类存具体类型信息 static class PhoneInfo { String number; int type; String label; } static class EmailInfo { String email; int type; String label; } static class AddressInfo { String fullAddress; int type; String street; String city; String postalCode; } static class SocialProfileInfo { String id; int type; String platformName; } } // 开始解析Cursor ContactDetails contact = new ContactDetails(); if (cursor != null && cursor.moveToFirst()) { do { // 先获取MIMETYPE String mimeType = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)); switch (mimeType) { case ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE: // 处理姓名 contact.fullName = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1)); // 如果需要名/姓,可以取DATA2/DATA3 break; case ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE: // 处理电话号码 ContactDetails.PhoneInfo phone = new ContactDetails.PhoneInfo(); phone.number = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1)); phone.type = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.DATA2)); phone.label = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA3)); contact.phones.add(phone); break; case ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE: // 处理邮箱 ContactDetails.EmailInfo email = new ContactDetails.EmailInfo(); email.email = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1)); email.type = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.DATA2)); email.label = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA3)); contact.emails.add(email); break; case ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE: // 处理地址 ContactDetails.AddressInfo address = new ContactDetails.AddressInfo(); address.fullAddress = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1)); address.type = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.DATA2)); address.street = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA3)); address.city = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA5)); address.postalCode = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA7)); contact.addresses.add(address); break; case ContactsContract.CommonDataKinds.Fax.CONTENT_ITEM_TYPE: // 处理传真,逻辑和电话类似 ContactDetails.PhoneInfo fax = new ContactDetails.PhoneInfo(); fax.number = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1)); fax.type = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.DATA2)); fax.label = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA3)); // 你可以单独存传真列表,或者加到phones里区分 break; case ContactsContract.CommonDataKinds.SocialProfile.CONTENT_ITEM_TYPE: // 处理社交账号,比如Facebook ID ContactDetails.SocialProfileInfo social = new ContactDetails.SocialProfileInfo(); social.id = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1)); social.type = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.DATA2)); social.platformName = ContactsContract.CommonDataKinds.SocialProfile.getTypeLabel(getResources(), social.type, "Unknown").toString(); contact.socialProfiles.add(social); break; // 其他类型可以继续添加case } } while (cursor.moveToNext()); // 关闭Cursor cursor.close(); }
几个关键注意点
- 权限问题:别忘了在Manifest中申请
READ_CONTACTS权限,并且在运行时动态申请(Android 6.0+)。 - Lookup Key优先:相比Contact ID,Lookup Key是联系人的永久标识,即使联系人被删除重建,Lookup Key可能保持不变,所以优先用它筛选更可靠。
- 类型转义:比如电话/邮箱的类型(DATA2)是整数,你可以用
ContactsContract.CommonDataKinds.Phone.getTypeLabel()把它转成友好的字符串(比如“移动电话”“工作邮箱”)。 - 空值处理:DATA字段可能为空,解析的时候要做非空判断,避免NullPointerException。
内容的提问来源于stack exchange,提问作者Sriram R
相关产品推荐
相关产品推荐

