Подтвердить что ты не робот

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

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

Благодарим вас в Advance.

4b9b3361

Ответ 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.

<я > Fun.

Это можно решить, например, путем отказа от вашего требования о том, что контакт должен иметь номер телефона и вместо этого разрешить "любой контакт с хотя бы номером телефона или адресом электронной почты":

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

или решить его, используя two Cursor s:

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

Ответ 2

Я рассмотрел ту же проблему. С тех пор я создаю собственное решение, которое вдохновлено этим сообщением, но немного другим. Теперь я хотел бы поделиться им как с первым ответом 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 без "@", m интересуется электронной почтой и телефоном. Мне требуется, чтобы данные 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. Также обратите внимание, что я сохраняю все Contact id в списке в том же порядке, что и контакты в курсоре. Позволяет называть этот список contactListId

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

В конце концов я запускаю список 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;
}

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

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

Коррекция/Улучшение

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

!mailKeySet.contains(key)

заменен на

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

с помощью клавиши телефона

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

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

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

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