android 如何在一个SQLite查询中获取联系人ID,电子邮件,电话号码?

7cwmlq89  于 2024-01-04  发布在  Android
关注(0)|答案(2)|浏览(202)

我想获取所有的联系人至少有一个电话号码,我也希望所有的电话号码和每个联系人的所有电子邮件。
当前代码:

  1. // To get All Contacts having atleast one phone number.
  2. Uri uri = ContactsContract.Contacts.CONTENT_URI;
  3. String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " > ?";
  4. String[] selectionArgs = new String[] {"0"};
  5. Cursor cu = applicationContext.getContentResolver().query(uri,
  6. null, selection, selectionArgs, null);
  7. // For getting All Phone Numbers and Emails further queries :
  8. while(cu.moveToNext()){
  9. String id = cu.getString(cu.getColumnIndex(ContactsContract.Contacts._ID));
  10. // To get Phone Numbers of Contact
  11. Cursor pCur = context.getContentResolver().query(
  12. ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
  13. new String[]{id}, null);
  14. // To get Email ids of Contact
  15. Cursor emailCur = context.getContentResolver().query(
  16. ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
  17. ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?",
  18. new String[]{id}, null);
  19. // Iterate through these cursors to get Phone numbers and Emails
  20. }

字符串
如果我的设备中有超过1000个联系人,则需要花费太多时间。如何在单个查询中获取所有数据,而不是为每个联系人执行两个额外查询?
有没有其他方法可以优化?
提前谢谢你。

zfciruhq

zfciruhq1#

ICS:当您从Data.CONTENT_URI查询时,您已经连接了相关Contact的所有数据列-也就是说,这会有效:

Java语言:

  1. ContentResolver resolver = getContentResolver();
  2. Cursor c = resolver.query(
  3. Data.CONTENT_URI,
  4. null,
  5. Data.HAS_PHONE_NUMBER + "!=0 AND (" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?)",
  6. new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
  7. Data.CONTACT_ID);
  8. while (c.moveToNext()) {
  9. long id = c.getLong(c.getColumnIndex(Data.CONTACT_ID));
  10. String name = c.getString(c.getColumnIndex(Data.DISPLAY_NAME));
  11. String data1 = c.getString(c.getColumnIndex(Data.DATA1));
  12. System.out.println(id + ", name=" + name + ", data1=" + data1);
  13. }

字符串

Kotlin:

  1. var c : Cursor? = null
  2. try {
  3. val resolver : ContentResolver = this.contentResolver //Replace "this" With Whatever The Reference To Your Activity Is (If Need Be)
  4. c = resolver.query(
  5. ContactsContract.Data.CONTENT_URI,
  6. null,
  7. ContactsContract.Data.HAS_PHONE_NUMBER + "!=0 AND (" + ContactsContract.Data.MIMETYPE + "=? OR " + ContactsContract.Data.MIMETYPE + "=?)",
  8. arrayOf(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE),
  9. ContactsContract.Data.CONTACT_ID)
  10. if (c!!.count > 0) {
  11. while (c.moveToNext()) {
  12. val id : Long = c.getLong(c.getColumnIndex(ContactsContract.Data.CONTACT_ID))
  13. val name : String = c.getString(c.getColumnIndex(ContactsContract.Data.DISPLAY_NAME))
  14. val data1 : String = c.getString(c.getColumnIndex(ContactsContract.Data.DATA1))
  15. Log.d("Tag", "$id) name=$name, data1=$data1")
  16. }
  17. }
  18. } finally {
  19. c?.close() //<-- close if not null
  20. }


如果您的目标是2.3,则需要考虑这样一个事实,即在查询Data时,HAS_PHONE_NUMBER无法通过所使用的连接获得。
Fun的值。“
例如,这可以通过跳过联系人“必须”有电话号码的要求,而满足于“任何至少有电话号码或电子邮件地址的联系人”来解决:

  1. Cursor c = resolver.query(
  2. Data.CONTENT_URI,
  3. null,
  4. Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?",
  5. new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
  6. Data.CONTACT_ID);


如果这不是一个选项,您可以随时选择一个可怕的黑客子选择:

  1. Cursor c = resolver.query(
  2. Data.CONTENT_URI,
  3. null,
  4. "(" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?) AND " +
  5. Data.CONTACT_ID + " IN (SELECT " + Contacts._ID + " FROM contacts WHERE " + Contacts.HAS_PHONE_NUMBER + "!=0)",
  6. new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE}, Data.CONTACT_ID);


或者通过使用两个Cursor s来求解它:

  1. Cursor contacts = resolver.query(Contacts.CONTENT_URI,
  2. null, Contacts.HAS_PHONE_NUMBER + " != 0", null, Contacts._ID + " ASC");
  3. Cursor data = resolver.query(Data.CONTENT_URI, null,
  4. Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?",
  5. new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
  6. Data.CONTACT_ID + " ASC");
  7. int idIndex = contacts.getColumnIndexOrThrow(Contacts._ID);
  8. int nameIndex = contacts.getColumnIndexOrThrow(Contacts.DISPLAY_NAME);
  9. int cidIndex = data.getColumnIndexOrThrow(Data.CONTACT_ID);
  10. int data1Index = data.getColumnIndexOrThrow(Data.DATA1);
  11. boolean hasData = data.moveToNext();
  12. while (contacts.moveToNext()) {
  13. long id = contacts.getLong(idIndex);
  14. System.out.println("Contact(" + id + "): " + contacts.getString(nameIndex));
  15. if (hasData) {
  16. long cid = data.getLong(cidIndex);
  17. while (cid <= id && hasData) {
  18. if (cid == id) {
  19. System.out.println("\t(" + cid + "/" + id + ").data1:" +
  20. data.getString(data1Index));
  21. }
  22. hasData = data.moveToNext();
  23. if (hasData) {
  24. cid = data.getLong(cidIndex);
  25. }
  26. }
  27. }
  28. }

展开查看全部
zed5wv10

zed5wv102#

我经历了完全相同的问题。从那时起,我建立了自己的解决方案,灵感来自这篇文章,但有点不同。现在我想分享它作为我的第一个StackOverFlow答案:-)
它与Jens建议的双光标方法非常相似。
1-从“联系人”表中获取相关联系人
2-获取相关联系人信息(邮件、电话等)
3-将这些结果合并
“相关”当然是由你决定的,但重要的一点是性能!此外,我相信其他解决方案使用非常适合的SQL查询也可以完成这项工作,但在这里我只想使用Android ContentProvider这里是代码:

部分常量

  1. public static String CONTACT_ID_URI = ContactsContract.Contacts._ID;
  2. public static String DATA_CONTACT_ID_URI = ContactsContract.Data.CONTACT_ID;
  3. public static String MIMETYPE_URI = ContactsContract.Data.MIMETYPE;
  4. public static String EMAIL_URI = ContactsContract.CommonDataKinds.Email.DATA;
  5. public static String PHONE_URI = ContactsContract.CommonDataKinds.Phone.DATA;
  6. public static String NAME_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Data.DISPLAY_NAME_PRIMARY : ContactsContract.Data.DISPLAY_NAME;
  7. public static String PICTURE_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Contacts.PHOTO_THUMBNAIL_URI : ContactsContract.Contacts.PHOTO_ID;
  8. public static String MAIL_TYPE = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
  9. public static String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;

字符串

1联系方式

这里我要求Contacts必须有不带“@”的name_NAME,并且它们的信息匹配给定的字符串(这些要求当然可以修改)。下面的方法的结果是第一个光标:

  1. public Cursor getContactCursor(String stringQuery, String sortOrder) {
  2. Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
  3. Logger.e(TAG, "ContactCursor search has started...");
  4. Long t0 = System.currentTimeMillis();
  5. Uri CONTENT_URI;
  6. if (stringQuery == null)
  7. CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
  8. else
  9. CONTENT_URI = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode(stringQuery));
  10. String[] PROJECTION = new String[]{
  11. CONTACT_ID_URI,
  12. NAME_URI,
  13. PICTURE_URI
  14. };
  15. String SELECTION = NAME_URI + " NOT LIKE ?";
  16. String[] SELECTION_ARGS = new String[]{"%" + "@" + "%"};
  17. Cursor cursor = sContext.getContentResolver().query(CONTENT_URI, PROJECTION, SELECTION, SELECTION_ARGS, sortOrder);
  18. Long t1 = System.currentTimeMillis();
  19. Logger.e(TAG, "ContactCursor finished in " + (t1 - t0) / 1000 + " secs");
  20. Logger.e(TAG, "ContactCursor found " + cursor.getCount() + " contacts");
  21. Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
  22. return cursor;
  23. }


正如您将看到的,这个查询的性能非常好!

2联系方式

现在让我们获取联系人信息。在这一点上,我不做任何链接之间已经取得的联系人和检索到的信息:我只是从数据表中获取所有信息...然而,为了避免无用的信息,我仍然需要没有“@”的名称,因为我对电子邮件和电话感兴趣,我需要数据MIMETYPE是MAIL_TYPE或PHONE_TYPE下面是代码:

  1. public Cursor getContactDetailsCursor() {
  2. Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
  3. Logger.e(TAG, "ContactDetailsCursor search has started...");
  4. Long t0 = System.currentTimeMillis();
  5. String[] PROJECTION = new String[]{
  6. DATA_CONTACT_ID_URI,
  7. MIMETYPE_URI,
  8. EMAIL_URI,
  9. PHONE_URI
  10. };
  11. String SELECTION = ContactManager.NAME_URI + " NOT LIKE ?" + " AND " + "(" + MIMETYPE_URI + "=? " + " OR " + MIMETYPE_URI + "=? " + ")";
  12. String[] SELECTION_ARGS = new String[]{"%" + "@" + "%", ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
  13. Cursor cursor = sContext.getContentResolver().query(
  14. ContactsContract.Data.CONTENT_URI,
  15. PROJECTION,
  16. SELECTION,
  17. SELECTION_ARGS,
  18. null);
  19. Long t1 = System.currentTimeMillis();
  20. Logger.e(TAG, "ContactDetailsCursor finished in " + (t1 - t0) / 1000 + " secs");
  21. Logger.e(TAG, "ContactDetailsCursor found " + cursor.getCount() + " contacts");
  22. Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
  23. return cursor;
  24. }


你会再次看到这个查询非常快.

3合并

现在让我们将联系人和他们各自的信息合并。我们的想法是使用HashMap(Key,String),其中Key是联系人id,String是任何你喜欢的(姓名,电子邮件,.)。
首先,我遍历Contact光标(按顺序排列),并将姓名和图片uri存储在两个不同的HashMap中。
我对联系人信息(邮件和电子邮件)做了同样的操作。但现在我注意到两个光标之间的相关性:如果电子邮件或电话的CONTACT_ID没有出现在contactListId中,它将被放在一边。我还检查电子邮件是否已经被遇到。注意,这种进一步的选择可能会在Name/Picture内容和Email/Phone HashMap内容之间引入不对称,但不用担心。
最后,我运行了contactListId列表,并构建了一个Contact对象列表,其中考虑了以下事实:联系人必须有信息(keySet条件),并且联系人必须至少有一封邮件或一封电子邮件(例如,如果联系人是Skype联系人,则可能会出现mail == null && phone == null的情况)。下面是代码:

  1. public List<Contact> getDetailedContactList(String queryString) {
  2. /**
  3. * First we fetch the contacts name and picture uri in alphabetical order for
  4. * display purpose and store these data in HashMap.
  5. */
  6. Cursor contactCursor = getContactCursor(queryString, NAME_URI);
  7. List<Integer> contactIds = new ArrayList<>();
  8. if (contactCursor.moveToFirst()) {
  9. do {
  10. contactIds.add(contactCursor.getInt(contactCursor.getColumnIndex(CONTACT_ID_URI)));
  11. } while (contactCursor.moveToNext());
  12. }
  13. HashMap<Integer, String> nameMap = new HashMap<>();
  14. HashMap<Integer, String> pictureMap = new HashMap<>();
  15. int idIdx = contactCursor.getColumnIndex(CONTACT_ID_URI);
  16. int nameIdx = contactCursor.getColumnIndex(NAME_URI);
  17. int pictureIdx = contactCursor.getColumnIndex(PICTURE_URI);
  18. if (contactCursor.moveToFirst()) {
  19. do {
  20. nameMap.put(contactCursor.getInt(idIdx), contactCursor.getString(nameIdx));
  21. pictureMap.put(contactCursor.getInt(idIdx), contactCursor.getString(pictureIdx));
  22. } while (contactCursor.moveToNext());
  23. }
  24. /**
  25. * Then we get the remaining contact information. Here email and phone
  26. */
  27. Cursor detailsCursor = getContactDetailsCursor();
  28. HashMap<Integer, String> emailMap = new HashMap<>();
  29. HashMap<Integer, String> phoneMap = new HashMap<>();
  30. idIdx = detailsCursor.getColumnIndex(DATA_CONTACT_ID_URI);
  31. int mimeIdx = detailsCursor.getColumnIndex(MIMETYPE_URI);
  32. int mailIdx = detailsCursor.getColumnIndex(EMAIL_URI);
  33. int phoneIdx = detailsCursor.getColumnIndex(PHONE_URI);
  34. String mailString;
  35. String phoneString;
  36. if (detailsCursor.moveToFirst()) {
  37. do {
  38. /**
  39. * We forget all details which are not correlated with the contact list
  40. */
  41. if (!contactIds.contains(detailsCursor.getInt(idIdx))) {
  42. continue;
  43. }
  44. if(detailsCursor.getString(mimeIdx).equals(MAIL_TYPE)){
  45. mailString = detailsCursor.getString(mailIdx);
  46. /**
  47. * We remove all double contact having the same email address
  48. */
  49. if(!emailMap.containsValue(mailString.toLowerCase()))
  50. emailMap.put(detailsCursor.getInt(idIdx), mailString.toLowerCase());
  51. } else {
  52. phoneString = detailsCursor.getString(phoneIdx);
  53. phoneMap.put(detailsCursor.getInt(idIdx), phoneString);
  54. }
  55. } while (detailsCursor.moveToNext());
  56. }
  57. contactCursor.close();
  58. detailsCursor.close();
  59. /**
  60. * Finally the contact list is build up
  61. */
  62. List<Contact> contacts = new ArrayList<>();
  63. Set<Integer> detailsKeySet = emailMap.keySet();
  64. for (Integer key : contactIds) {
  65. if(!detailsKeySet.contains(key) || (emailMap.get(key) == null && phoneMap.get(key) == null))
  66. continue;
  67. contacts.add(new Contact(String.valueOf(key), pictureMap.get(key), nameMap.get(key), emailMap.get(key), phoneMap.get(key)));
  68. }
  69. return contacts;
  70. }


Contact对象的定义由您决定。
希望这对你有帮助,并感谢前一篇文章。

纠正/改进

我忘了检查手机按键设置:它应该看起来像

  1. !mailKeySet.contains(key)


改为

  1. (!mailKeySet.contains(key) && !phoneKeySet.contains(key))


使用电话键集

  1. Set<Integer> phoneKeySet = phoneMap.keySet();


我为什么不添加一个空的联系人光标检查如下:

  1. if(contactCursor.getCount() == 0){
  2. contactCursor.close();
  3. return new ArrayList<>();
  4. }


就在getContactCursor调用之后

展开查看全部

相关问题