如何获得联系人ID,电子邮件,电话号码在一个SQLite查询? 联系Android优化

我想用一个电话号码至less获取所有联系人,同时我也希望每个联系人的所有电话号码和所有电子邮件。

当前代码:

// To get All Contacts having atleast one phone number. Uri uri = ContactsContract.Contacts.CONTENT_URI; String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " > ?"; String[] selectionArgs = new String[] {"0"}; Cursor cu = applicationContext.getContentResolver().query(uri, null, selection, selectionArgs, null); // For getting All Phone Numbers and Emails further queries : while(cu.moveToNext()){ String id = cu.getString(cu.getColumnIndex(ContactsContract.Contacts._ID)); // To get Phone Numbers of Contact Cursor pCur = context.getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?", new String[]{id}, null); // To get Email ids of Contact Cursor emailCur = context.getContentResolver().query( ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[]{id}, null); // Iterate through these cursors to get Phone numbers and Emails } 

如果我的设备中有超过1000个联系人,则需要花费太多时间。 如何在单个查询中获取所有数据,而不是为每个联系人执行两个附加查询?

还是有其他的方法来优化?

先谢谢你。

Solutions Collecting From Web of "如何获得联系人ID,电子邮件,电话号码在一个SQLite查询? 联系Android优化"

ICS:当你从Data.CONTENT_URI查询时,你有相关的Contact所有行已经join – 即这将工作:

 ContentResolver resolver = getContentResolver(); Cursor c = resolver.query( Data.CONTENT_URI, null, Data.HAS_PHONE_NUMBER + "!=0 AND (" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?)", new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE}, Data.CONTACT_ID); while (c.moveToNext()) { long id = c.getLong(c.getColumnIndex(Data.CONTACT_ID)); String name = c.getString(c.getColumnIndex(Data.DISPLAY_NAME)); String data1 = c.getString(c.getColumnIndex(Data.DATA1)); System.out.println(id + ", name=" + name + ", data1=" + data1); } 

如果您的目标是2.3,则需要说明在查询Data时使用的连接不可用HAS_PHONE_NUMBER

好玩

例如,这可以通过跳过您的要求,联系人必须有一个电话号码,而是解决“与至less一个电话号码或电子邮件地址的任何联系”解决:

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

如果这不是一个select,你总是可以去一个可怕的黑客子select:

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

或者使用两个 Cursor来解决它:

 Cursor contacts = resolver.query(Contacts.CONTENT_URI, null, Contacts.HAS_PHONE_NUMBER + " != 0", null, Contacts._ID + " ASC"); Cursor data = resolver.query(Data.CONTENT_URI, null, Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?", new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE}, Data.CONTACT_ID + " ASC"); int idIndex = contacts.getColumnIndexOrThrow(Contacts._ID); int nameIndex = contacts.getColumnIndexOrThrow(Contacts.DISPLAY_NAME); int cidIndex = data.getColumnIndexOrThrow(Data.CONTACT_ID); int data1Index = data.getColumnIndexOrThrow(Data.DATA1); boolean hasData = data.moveToNext(); while (contacts.moveToNext()) { long id = contacts.getLong(idIndex); System.out.println("Contact(" + id + "): " + contacts.getString(nameIndex)); if (hasData) { long cid = data.getLong(cidIndex); while (cid <= id && hasData) { if (cid == id) { System.out.println("\t(" + cid + "/" + id + ").data1:" + data.getString(data1Index)); } hasData = data.moveToNext(); if (hasData) { cid = data.getLong(cidIndex); } } } } 

我经历了完全相同的问题。 从那以后,我创build了自己的解决scheme,这个灵感来自于这篇文章,但有点不同。 现在我想分享它作为我的第一个StackOverFlow答案:-)

它与Jens提出的双光标方法非常相似。 这个想法是

1-从联系人表中获取相关联系人
2-获取相关联系人信息(邮件,电话…)
3-结合这些结果

这个“相关”当然是由你自己决定的,但重要的一点就是表演! 此外,我相信其他解决scheme使用合适的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联系

在这里,我要求联系人必须DISPLAY_NAME没有“@”,并且他们的信息匹配给定的string(这些要求当然可以修改)。 以下方法的结果是第一个游标:

 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(见常量)。 这里是代码:

 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是联系人ID,String是你喜欢的任何东西(名字,email,…)。

首先,我运行Contact光标(按字母顺序排列),并将名称和图片uri存储在两个不同的HashMap中。 还请注意,我将所有联系人ID存储在列表中的顺序与联系人出现在光标中的顺序完全相同。 让我们调用这个列表contactListId

我也为联系信息(邮件和电子邮件)做同样的事情。 但是现在我关心两个游标之间的相关性:如果电子邮件或电话的CONTACT_ID没有出现在contactListId中,则将其放在一边。 我也检查是否已经遇到电子邮件。 请注意,这种进一步的select可能会在名称/图片内容和电子邮件/电话HashMap内容之间引入不对称,但不要担心。

最后,我运行contactListId列表,并build立一个Contact对象列表,负责处理以下事实:联系人必须具有信息(keySet条件),联系人必须至less有一个邮件或一个电子邮件(邮件= = null && phone == null如果联系人是Skype联系人,则可能会出现)。 这里是代码:

 public List<Contact> getDetailedContactList(String queryString) { /** * First we fetch the contacts name and picture uri in alphabetical order for * display purpose and store these data in HashMap. */ 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()); } /** * Then we get the remaining contact information. Here email and phone */ 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 { /** * We forget all details which are not correlated with the contact list */ if (!contactIds.contains(detailsCursor.getInt(idIdx))) { continue; } if(detailsCursor.getString(mimeIdx).equals(MAIL_TYPE)){ mailString = detailsCursor.getString(mailIdx); /** * We remove all double contact having the same email address */ 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(); /** * Finally the contact list is build up */ 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调用之后