我曾经遇到过完全相同的问题,之后我构建了自己的解决方案,灵感来自于这篇文章,但也有些不同。现在我想把它作为我的第一个 StackOverFlow 答案分享出来 :-)
这个方法与 Jens 建议的双光标方法非常相似。其思路是:
1- 从联系人表中获取相关的联系人
2- 获取相关联系人的信息(邮件、电话等)
3- 将这些结果合并
"相关"当然取决于你的需求,但最重要的是性能!此外,我确信使用适当的 SQL 查询可能也能完成任务,但我只想使用 Android ContentProvider。
以下是代码:
一些常量
public static String CONTACT_ID_URI = ContactsContract.Contacts._ID;
public static String DATA_CONTACT_ID_URI = ContactsContract.Data.CONTACT_ID;
public static String MIMETYPE_URI = ContactsContract.Data.MIMETYPE;
public static String EMAIL_URI = ContactsContract.CommonDataKinds.Email.DATA;
public static String PHONE_URI = ContactsContract.CommonDataKinds.Phone.DATA;
public static String NAME_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Data.DISPLAY_NAME_PRIMARY : ContactsContract.Data.DISPLAY_NAME;
public static String PICTURE_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Contacts.PHOTO_THUMBNAIL_URI : ContactsContract.Contacts.PHOTO_ID;
public static String MAIL_TYPE = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
public static String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
1 联系人
这里要求联系人的显示名称中不能包含"@"符号,并且他们的信息与给定的字符串匹配(当然可以修改这些要求)。以下方法的结果是第一个游标:
public Cursor getContactCursor(String stringQuery, String sortOrder) {
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Logger.e(TAG, "ContactCursor search has started...");
Long t0 = System.currentTimeMillis();
Uri CONTENT_URI;
if (stringQuery == null)
CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
else
CONTENT_URI = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode(stringQuery));
String[] PROJECTION = new String[]{
CONTACT_ID_URI,
NAME_URI,
PICTURE_URI
};
String SELECTION = NAME_URI + " NOT LIKE ?";
String[] SELECTION_ARGS = new String[]{"%" + "@" + "%"};
Cursor cursor = sContext.getContentResolver().query(CONTENT_URI, PROJECTION, SELECTION, SELECTION_ARGS, sortOrder);
Long t1 = System.currentTimeMillis();
Logger.e(TAG, "ContactCursor finished in " + (t1 - t0) / 1000 + " secs");
Logger.e(TAG, "ContactCursor found " + cursor.getCount() + " contacts");
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
这个查询非常高效,你会看到的!
2 联系方式
现在让我们获取联系人信息。此时,我不会将已经获取的联系人与检索到的信息进行任何关联:我只是从数据表中提取所有信息...然而,为了避免无用的信息,我仍然需要要求 DISPLAY_NAMES 不包含“@”符号,并且由于我对电子邮件和电话感兴趣,所以要求数据 MIMETYPE 为 MAIL_TYPE 或 PHONE_TYPE(请参阅 Constants)。以下是代码:
public Cursor getContactDetailsCursor() {
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Logger.e(TAG, "ContactDetailsCursor search has started...");
Long t0 = System.currentTimeMillis();
String[] PROJECTION = new String[]{
DATA_CONTACT_ID_URI,
MIMETYPE_URI,
EMAIL_URI,
PHONE_URI
};
String SELECTION = ContactManager.NAME_URI + " NOT LIKE ?" + " AND " + "(" + MIMETYPE_URI + "=? " + " OR " + MIMETYPE_URI + "=? " + ")";
String[] SELECTION_ARGS = new String[]{"%" + "@" + "%", ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
Cursor cursor = sContext.getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
PROJECTION,
SELECTION,
SELECTION_ARGS,
null);
Long t1 = System.currentTimeMillis();
Logger.e(TAG, "ContactDetailsCursor finished in " + (t1 - t0) / 1000 + " secs");
Logger.e(TAG, "ContactDetailsCursor found " + cursor.getCount() + " contacts");
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
再一次,您将看到这个查询非常快...
3 组合
现在让我们将联系人和他们各自的信息组合起来。想法是使用HashMap(Key,String)其中Key是Contact id,String是任何您喜欢的内容(name,email等)。
首先,我遍历Contact游标(按字母顺序排序),并将名称和图片uri存储在两个不同的HashMap中。注意,我还以相同的顺序将所有Contact id存储在列表中。让我们称之为contactListId
我对Contact信息(邮件和电子邮件)执行相同的操作。但是现在我要处理两个游标之间的相关性:如果邮件或电话的CONTACT_ID没有出现在contactListId中,则会被放置在一旁。我还检查电子邮件是否已经被遇到过。请注意,这进一步选择可能会在Name / Picture内容与Email / Phone HashMap内容之间引入不对称,但不用担心。
最后,我遍历contactListId列表,并构建一个Contact对象列表,要注意以下几点:联系人必须具有信息(keySet条件),并且联系人必须至少有一个邮件或一个电子邮件(如果该联系人是Skype联系人,则可能出现mail == null && phone == null的情况)。 下面是代码:
public List<Contact> getDetailedContactList(String queryString) {
Cursor contactCursor = getContactCursor(queryString, NAME_URI);
List<Integer> contactIds = new ArrayList<>();
if (contactCursor.moveToFirst()) {
do {
contactIds.add(contactCursor.getInt(contactCursor.getColumnIndex(CONTACT_ID_URI)));
} while (contactCursor.moveToNext());
}
HashMap<Integer, String> nameMap = new HashMap<>();
HashMap<Integer, String> pictureMap = new HashMap<>();
int idIdx = contactCursor.getColumnIndex(CONTACT_ID_URI);
int nameIdx = contactCursor.getColumnIndex(NAME_URI);
int pictureIdx = contactCursor.getColumnIndex(PICTURE_URI);
if (contactCursor.moveToFirst()) {
do {
nameMap.put(contactCursor.getInt(idIdx), contactCursor.getString(nameIdx));
pictureMap.put(contactCursor.getInt(idIdx), contactCursor.getString(pictureIdx));
} while (contactCursor.moveToNext());
}
Cursor detailsCursor = getContactDetailsCursor();
HashMap<Integer, String> emailMap = new HashMap<>();
HashMap<Integer, String> phoneMap = new HashMap<>();
idIdx = detailsCursor.getColumnIndex(DATA_CONTACT_ID_URI);
int mimeIdx = detailsCursor.getColumnIndex(MIMETYPE_URI);
int mailIdx = detailsCursor.getColumnIndex(EMAIL_URI);
int phoneIdx = detailsCursor.getColumnIndex(PHONE_URI);
String mailString;
String phoneString;
if (detailsCursor.moveToFirst()) {
do {
if (!contactIds.contains(detailsCursor.getInt(idIdx))) {
continue;
}
if(detailsCursor.getString(mimeIdx).equals(MAIL_TYPE)){
mailString = detailsCursor.getString(mailIdx);
if(!emailMap.containsValue(mailString.toLowerCase()))
emailMap.put(detailsCursor.getInt(idIdx), mailString.toLowerCase());
} else {
phoneString = detailsCursor.getString(phoneIdx);
phoneMap.put(detailsCursor.getInt(idIdx), phoneString);
}
} while (detailsCursor.moveToNext());
}
contactCursor.close();
detailsCursor.close();
List<Contact> contacts = new ArrayList<>();
Set<Integer> detailsKeySet = emailMap.keySet();
for (Integer key : contactIds) {
if(!detailsKeySet.contains(key) || (emailMap.get(key) == null && phoneMap.get(key) == null))
continue;
contacts.add(new Contact(String.valueOf(key), pictureMap.get(key), nameMap.get(key), emailMap.get(key), phoneMap.get(key)));
}
return contacts;
}
联系人对象的定义由您自己决定。
希望这可以帮到您,感谢之前的帖子。
更正/改进:
我忘记检查电话键集:它应该更像。
!mailKeySet.contains(key)
替换为
(!mailKeySet.contains(key) && !phoneKeySet.contains(key))
使用手机keySet
Set<Integer> phoneKeySet = phoneMap.keySet();
我为什么不添加一个空联系人光标检查,例如:
if(contactCursor.getCount() == 0){
contactCursor.close();
return new ArrayList<>();
}
在调用getContactCursor之后