【问题标题】:How to get Contact ID, Email, Phone number in one SQLite query ? Contacts Android Optimization如何在一次 SQLite 查询中获取联系人 ID、电子邮件、电话号码?联系人 Android 优化
【发布时间】:2012-08-05 07:47:10
【问题描述】:

我想至少用一个电话号码获取所有联系人,我还想要每个联系人的所有电话号码和所有电子邮件。

当前代码:

// 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 个联系人,这将花费太多时间。如何在单个查询中获取所有数据,而不是为每个联系人执行两个额外的查询?

或者有没有其他优化方法?

在此先感谢您。

【问题讨论】:

  • 在启动您的应用程序时,第一次处理此代码并将所有数据方式、电子邮件、phoennumber 保存在静态数组列表中,您可以从中获取所需的每个数据
  • @jenuine :我正在这样做,当联系人发生一些变化时,此代码会出现在图片中,例如添加、删除、修改新联系人。
  • 是的。这是个问题,但我没用过,android中的contentobserver,但我相信这个问题可以解决..

标签: android sqlite android-contacts contactscontract


【解决方案1】:

ICS:当您从 Data.CONTENT_URI 查询时,关联的 Contact 中的所有行都已加入 - 即这会起作用:

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,则需要考虑到 HAS_PHONE_NUMBER 无法通过查询 Data 时使用的连接获得。

有趣

例如,这可以通过跳过联系人必须有电话号码的要求来解决,而是选择“任何至少有电话号码或电子邮件地址的联系人” ":

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);

如果这不是一个选项,你总是可以选择一个可怕的 hacky 子选择:

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);

或者用两个Cursors:

解决
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);
            }
        }
    }
}

【讨论】:

  • 这太棒了!但是我想知道如果我将光标用于电话和电子邮件,我如何区分 data1 是电话还是电子邮件?
  • 您检查Data.MIMETYPE 的值是Email.CONTENT_ITEM_TYPE 还是Phone.CONTENT_ITEM_TYPE
  • 嗨,詹斯。你能回答这个问题吗? stackoverflow.com/questions/19699578/…@Jens
  • 嗨 jens,我如何检查它是 Email.CONTENT_ITEM_TYPE 还是 Phone.CONTENT_ITEM_TYPE。
  • 谢谢它也对我有用,但我还需要获取电话类型。如何获取电话类型。
【解决方案2】:

我遇到了完全相同的问题。从那以后,我构建了自己的解决方案,该解决方案的灵感来自这篇文章,但有点不同。现在我想分享它作为我的第一个 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 个联系人

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

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 是您喜欢的任何内容(姓名、电子邮件、...)。

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

我对联系信息(邮件和电子邮件)也是如此。但是现在我处理了两个光标之间的相关性:如果电子邮件或电话的 CONTACT_ID 没有出现在 contactListId 中,则将其放在一边。我还检查是否已经遇到过电子邮件。请注意,此进一步选择可能会在名称/图片内容和电子邮件/电话 HashMap 内容之间引入不对称,但不要担心。

最后,我遍历了 contactListId 列表并构建了一个 Contact 对象列表,考虑到以下事实:联系人必须有信息(keySet 条件)并且联系人必须至少有一封邮件或电子邮件(案例例如,如果联系人是 Skype 联系人,则可能会出现 mail == null && phone == null)。 这是代码:

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))

用手机按键设置

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

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

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

在 getContactCursor 调用之后

【讨论】:

  • ContactManager.NAME_URIContactManager 应该被删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-30
  • 2014-10-16
  • 2016-12-26
相关资源
最近更新 更多