簡體   English   中英

如何在一個SQLite查詢中獲取聯系人ID,電子郵件,電話號碼? 聯系人Android優化

[英]How to get Contact ID, Email, Phone number in one SQLite query ? Contacts Android Optimization

我希望至少使用一個電話號碼獲取所有聯系人,我也想要每個聯系人的所有電話號碼和所有電子郵件。

當前代碼:

// 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個聯系人,則需要花費太多時間。 如何在單個查詢中獲取所有數據,而不是為每個聯系人執行兩個額外查詢?

或者還有其他方法可以優化嗎?

先感謝您。

ICS:當你從Data.CONTENT_URI查詢時,你已經Contact了相關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,則需要考慮通過查詢Data時使用的連接無法使用HAS_PHONE_NUMBER這一事實。

.

例如,可以通過跳過您的聯系人必須擁有電話號碼的要求來解決這個問題,而不是滿足於“與至少一個電話號碼或電子郵件地址的任何聯系”:

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

或者使用兩個 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);
            }
        }
    }
}

我經歷了完全相同的問題。 從那時起,我構建了自己的解決方案,這個解決方案的靈感來自這篇文 現在我想分享它作為我的第一個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是Contact id,String是你喜歡的(name,email,...)。

首先,我運行Contact游標(按字母順序排列)並將名稱和圖片uri存儲在兩個不同的HashMap中。 另請注意,我將所有聯系人ID存儲在列表中,其順序與聯系人在游標中出現的順序相同。 讓我們調用這個列表contactListId

我對聯系信息(郵件和電子郵件)也這樣做。 但是現在我處理兩個光標之間的相關性:如果電子郵件或電話的CONTACT_ID沒有出現在contactListId中,它就被放在一邊。 我還檢查是否已經遇到電子郵件。 請注意,此進一步選擇可能會在名稱/圖片內容和電子郵件/電話HashMap內容之間引入不對稱,但不要擔心。

最后,我遍歷contactListId列表並構建一個Contact對象列表,其中包含以下事實:聯系人必須具有信息(keySet條件),並且聯系人必須至少包含郵件或電子郵件(mail =的情況=如果聯系人是Skype聯系人,則可能會出現= 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;
}

Contact對象定義取決於您。

希望這對以前的帖子有所幫助和感謝。

校正/改進

我忘記檢查手機按鍵了:它應該看起來像

!mailKeySet.contains(key)

取而代之

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

用手機鍵設置

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

我為什么不添加一個空的聯系人光標檢查,如:

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

在getContactCursor調用之后

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM