Как получить идентификатор контакта, адрес электронной почты, номер телефона в одном запросе SQLite? Контакты Оптимизация Android

Я хочу получить все контакты по крайней мере с одним номером телефона, также я хочу, чтобы все номера телефонов и все электронные письма для каждого контакта.

Текущий код:

// 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 контактов, это занимает слишком много времени. Как я могу получить все данные в одном запросе, а не делать два дополнительных запроса для каждого контакта?

Или есть другие способы оптимизации?

Заранее спасибо.


person Sagar    schedule 08.08.2012    source источник
comment
при запуске приложения обработайте этот код в первый раз и сохраните все эти данные, электронную почту, номер телефона в статическом массиве, и из этого вы можете получить все данные в соответствии с вашими потребностями.   -  person januprasad    schedule 08.08.2012
comment
@jenuine: Я делаю это, этот код появляется, когда в Контактах происходят какие-то изменения, например, новый контакт добавлен, удален, изменен.   -  person Sagar    schedule 08.08.2012
comment
Да, это проблема, но я не использовал contentobserver в Android, но я уверен, что эту проблему можно решить.   -  person januprasad    schedule 08.08.2012


Ответы (2)


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

Если это не вариант, вы всегда можете пойти на ужасно хакерский подвыбор:

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);
            }
        }
    }
}
person Jens    schedule 08.08.2012
comment
Это здорово! Однако мне интересно, если я комбинирую курсор для телефона и электронной почты, как я могу различить, является ли data1 телефоном или электронной почтой? - person Rendy; 07.10.2013
comment
Вы проверяете, является ли значение Data.MIMETYPE либо Email.CONTENT_ITEM_TYPE, либо Phone.CONTENT_ITEM_TYPE. - person Jens; 08.10.2013
comment
привет дженс. не могли бы вы ответить на этот вопрос? Заголовок stackoverflow.com/questions/19699578/ @Jens - person AndroidDev; 31.10.2013
comment
привет jens, Как я могу проверить, является ли это Email.CONTENT_ITEM_TYPE или Phone.CONTENT_ITEM_TYPE. - person KarnakerReddy Gaddampally; 06.08.2014
comment
Спасибо. Это сработало и для меня, но мне также нужно получить тип телефона. Как я могу получить тип телефона. - person KarnakerReddy Gaddampally; 07.08.2014
comment
Вам нужно получить тип mimetype следующим образом: String mimeType = data.getString(data.getColumnIndex(ContactsContract.Data.MIMETYPE)); а затем вы можете выполнить тест if mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) - person user1007522; 04.04.2017

Я прошел через точно такую ​​же проблему. С тех пор я создаю свое собственное решение, вдохновленное этим постом, но немного отличающееся. Теперь я хотел бы поделиться им как своим первым ответом на StackOverFlow :-)

Это очень похоже на подход с двойным курсором, предложенный Йенсом. Идея состоит в том, чтобы

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 — это идентификатор контакта, а String — все, что вам нравится (имя, адрес электронной почты,...).

Во-первых, я запускаю курсор «Контакт» (который расположен в алфавитном порядке) и сохраняю имена и uri-изображения в двух разных HashMap. Также обратите внимание, что я храню все идентификаторы контактов в списке в том же порядке, в котором контакты отображаются в курсоре. Давайте назовем этот список contactListId

Я делаю то же самое для контактной информации (почта и электронная почта). Но теперь я позабочусь о корреляции между двумя курсорами: если CONTACT_ID электронной почты или телефона не отображается в contactListId, он откладывается. Я также проверяю, встречалось ли уже письмо. Обратите внимание, что этот дополнительный выбор может привести к асимметрии между содержимым Name/Picture и содержимым HashMap электронной почты/телефона, но не беспокойтесь.

В конце концов, я пробегаюсь по списку contactListId и создаю список объекта Contact, учитывая тот факт, что: контакт должен иметь информацию (условие keySet) и что контакт должен иметь по крайней мере почту или адрес электронной почты (случай, когда mail = = 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;
}

Определение объекта Contact зависит от вас.

Надеюсь, это поможет и спасибо за предыдущий пост.

Исправление/улучшение

Я забыл проверить набор ключей телефона: он должен выглядеть примерно так

!mailKeySet.contains(key)

заменяется

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

с помощью ключа телефонаSet

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

Я почему бы не добавить проверку пустого контактного курсора, например:

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

сразу после вызова getContactCursor

person Phil    schedule 03.07.2015
comment
В ContactManager.NAME_URI следует удалить ContactManager. - person Sherlock; 10.03.2016