13

Is there any way to shorten this time? I'm running with the cursor and takes the name, phone numbers and emails

if I remove the phone numbers query from the query loop it ends in 3 seconds

any idea how can I improve that query?

Maybe I'm doing something wrong in my query?

(Obviously I'm doing it async but still... it's a very long time that a user can't wait)

Hope someone can share his thoughts about this

this is my code

ContentResolver cr = getContentResolver();
            Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
                    null, null, null, null);
            if (cur.getCount() > 0) {
                while (cur.moveToNext()) {
                    AddressBookEntity adr = new AddressBookEntity();
                    String id = cur.getString(cur
                            .getColumnIndex(ContactsContract.Contacts._ID));
                    String name = cur
                            .getString(cur
                                    .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
                    adr.fullName = name;

                    Cursor emailCur = cr
                            .query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
                                    null,
                                    ContactsContract.CommonDataKinds.Email.CONTACT_ID
                                            + " = ?", new String[] { id },
                                    null);
                    while (emailCur.moveToNext()) {
                        // This would allow you get several email addresses
                        // if the email addresses were stored in an array
                        String email = emailCur
                                .getString(emailCur
                                        .getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
                        if (!Utils.IsNullOrEmptyString(email)) {
                            adr.email = email;
                        }

                    }
                    emailCur.close();

                    if (Integer
                            .parseInt(cur.getString(cur
                                    .getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
                        Cursor pCur = cr
                                .query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                                        null,
                                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID
                                                + " = ?",
                                        new String[] { id }, null);
                        int phoneIndex = 0;
                        while (pCur.moveToNext()) {
                            String number = pCur.getString(pCur
                                    .getColumnIndex(Phone.NUMBER));

                            String country = Utils.GetCountryFromNumber(
                                    number, app);
                            number = Utils.GetFullPhoneNumber(number, app);
                            if (phoneIndex == 0) {
                                if (!Utils.IsNullOrEmptyString(number)) {
                                    adr.contactAdressBookId = id;
                                    adr.phoneNumber = number;
                                    adr.userInsertedId = app.userCred.userId;
                                    adr.country = country;
                                    myContacts.add(adr);
                                }
                            } else {
                                if (!Utils.IsNullOrEmptyString(number)) {
                                    AddressBookEntity adrMore = new AddressBookEntity();
                                    adrMore.fullName = adrMore.fullName;
                                    adrMore.country = adrMore.country;
                                    adrMore.email = adrMore.email;
                                    adrMore.phoneNumber = number;
                                    adrMore.contactAdressBookId = id;
                                    adrMore.country = country;
                                    myContacts.add(adrMore);
                                }
                            }
                        }
                        pCur.close();
                    }
                }
                cur.close();
itay83
  • 410
  • 1
  • 6
  • 18

3 Answers3

45

with the following code for 59 contacts i got the following results on the emulator:

      D  ╔══════ query execution stats ═══════
      D  ║    got 59 contacts
      D  ║    query took 0.012 s (12 ms)
      D  ╚════════════════════════════════════

ok, that was the best time, but the average is 25-35 ms (for 59 contacts), add the following code in some onClick callback and run in several times in order to get the average time, in your case you should get 30 * 700 / 59 = ~300-400 ms, not 3 seconds, let alone one minute ;)

it uses Uri set to Contactables.CONTENT_URI added in API level 18 but you can use ContactsContract.Data.CONTENT_URI when building for pre 18 API devices

List<AddressBookContact> list = new LinkedList<AddressBookContact>();
LongSparseArray<AddressBookContact> array = new LongSparseArray<AddressBookContact>();
long start = System.currentTimeMillis();

String[] projection = {
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.CONTACT_ID,
        ContactsContract.Contacts.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Contactables.DATA,
        ContactsContract.CommonDataKinds.Contactables.TYPE,
};
String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)";
String[] selectionArgs = {
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
};
String sortOrder = ContactsContract.Contacts.SORT_KEY_ALTERNATIVE;

Uri uri = ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
// we could also use Uri uri = ContactsContract.Data.CONTENT_URI;

// ok, let's work...
Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

final int mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE);
final int idIdx = cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
final int nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
final int dataIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.DATA);
final int typeIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.TYPE);

while (cursor.moveToNext()) {
    long id = cursor.getLong(idIdx);
    AddressBookContact addressBookContact = array.get(id);
    if (addressBookContact == null) {
        addressBookContact = new AddressBookContact(id, cursor.getString(nameIdx), getResources());
        array.put(id, addressBookContact);
        list.add(addressBookContact);
    }
    int type = cursor.getInt(typeIdx);
    String data = cursor.getString(dataIdx);
    String mimeType = cursor.getString(mimeTypeIdx);
    if (mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
        // mimeType == ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
        addressBookContact.addEmail(type, data);
    } else {
        // mimeType == ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
        addressBookContact.addPhone(type, data);
    }
}
long ms = System.currentTimeMillis() - start;
cursor.close();

// done!!! show the results...
int i = 1;
for (AddressBookContact addressBookContact : list) {
    Log.d(TAG, "AddressBookContact #" + i++ + ": " + addressBookContact.toString(true));
}
final String cOn = "<b><font color='#ff9900'>";
final String cOff = "</font></b>";
Spanned l1 = Html.fromHtml("got " + cOn + array.size() + cOff + " contacts<br/>");
Spanned l2 = Html.fromHtml("query took " + cOn + ms / 1000f + cOff + " s (" + cOn + ms + cOff + " ms)");

Log.d(TAG, "\n\n╔══════ query execution stats ═══════" );
Log.d(TAG, "║    " + l1);
Log.d(TAG, "║    " + l2);
Log.d(TAG, "╚════════════════════════════════════" );
SpannableStringBuilder msg = new SpannableStringBuilder().append(l1).append(l2);

LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
TextView tv = new TextView(this);
tv.setTextSize(20);
tv.setBackgroundColor(0xff000033);
tv.setPadding(24, 8, 24, 24);
tv.setText(msg);
ll.addView(tv);
ListView lv = new ListView(this);
lv.setAdapter(new ArrayAdapter<AddressBookContact>(this, android.R.layout.simple_list_item_1, list));
ll.addView(lv);
new AlertDialog.Builder(this).setView(ll).setPositiveButton("close", null).create().show();

the helper AddressBookContact class:

class AddressBookContact {
    private long id;
    private Resources res;
    private String name;
    private LongSparseArray<String> emails;
    private LongSparseArray<String> phones;

    AddressBookContact(long id, String name, Resources res) {
        this.id = id;
        this.name = name;
        this.res = res;
    }

    @Override
    public String toString() {
        return toString(false);
    }

    public String toString(boolean rich) {
        SpannableStringBuilder builder = new SpannableStringBuilder();
        if (rich) {
            builder.append("id: ").append(Long.toString(id))
                    .append(", name: ").append("\u001b[1m").append(name).append("\u001b[0m");
        } else {
            builder.append(name);
        }

        if (phones != null) {
            builder.append("\n\tphones: ");
            for (int i = 0; i < phones.size(); i++) {
                int type = (int) phones.keyAt(i);
                builder.append(ContactsContract.CommonDataKinds.Phone.getTypeLabel(res, type, ""))
                        .append(": ")
                        .append(phones.valueAt(i));
                if (i + 1 < phones.size()) {
                    builder.append(", ");
                }
            }
        }

        if (emails != null) {
            builder.append("\n\temails: ");
            for (int i = 0; i < emails.size(); i++) {
                int type = (int) emails.keyAt(i);
                builder.append(ContactsContract.CommonDataKinds.Email.getTypeLabel(res, type, ""))
                        .append(": ")
                        .append(emails.valueAt(i));
                if (i + 1 < emails.size()) {
                    builder.append(", ");
                }
            }
        }
        return builder.toString();
    }

    public void addEmail(int type, String address) {
        if (emails == null) {
            emails = new LongSparseArray<String>();
        }
        emails.put(type, address);
    }

    public void addPhone(int type, String number) {
        if (phones == null) {
            phones = new LongSparseArray<String>();
        }
        phones.put(type, number);
    }
}
pskink
  • 23,874
  • 6
  • 66
  • 77
  • Thanks alot gonna try that :) will get back to you with my results – itay83 Nov 09 '14 at 08:26
  • and? what was your results? – pskink Nov 09 '14 at 15:57
  • not yet I'm on some other project will get back to check this later i'll keep you posted :) and thanks again! from what I see from u're results my 700 contacts should load in few seconds hope this will actually be the result – itay83 Nov 09 '14 at 18:02
  • Yes, I'm interested in the results too, so please let me know when you test it... – pskink Nov 09 '14 at 18:07
  • don't worry I sure will! :) – itay83 Nov 09 '14 at 18:56
  • 1
    Hey it's working great!!! 11-11 10:00:08.907: D/TAG(32169): ╔══════ query execution stats ═══════ 11-11 10:00:08.907: D/TAG(32169): ║ got 959 contacts 11-11 10:00:08.917: D/TAG(32169): ║ query took 1.985 s (1985 ms) 11-11 10:00:08.917: D/TAG(32169): ╚════════════════════════════════════ Thanks alot! gonna use it :)) – itay83 Nov 11 '14 at 08:01
  • hmm, you must have very slow device since 1985 ms per 700 contacts is ~3 ms / contact, and my emulator did 60 contacts in 12 ms so its 0.2 ms / contact... btw what device is it? also is it an average time based on multiple runs or just first run? – pskink Nov 11 '14 at 08:06
  • It's the result of the first run... I run this on samsung galaxy 5 I'm now integrating it into my code so I will run it (bit customly) multiple times You really helped me with this code Thanks alot again – itay83 Nov 11 '14 at 08:23
  • hi, and what was the average time in your case? – pskink Nov 16 '14 at 17:45
  • This is definitely very quick and effective, I'm having a bit of trouble figuring out how to get the address/ location (StructuredPostal is the commonDataKinds). Anyone have any advice? – PGMacDesign Nov 04 '15 at 21:33
  • @Silmarilos change `selection` and `selectionArgs` for this new `CommonDataKinds` – pskink Nov 04 '15 at 21:37
  • Great answer and perfect outout. I have only 1 question -> how can i get only email address on listview click ? – rushank shah Jun 02 '16 at 18:09
  • @rushankshah use `AddressBookContact.emails` field then – pskink Jun 02 '16 at 18:17
  • Yes. i have use emails field but when i click on listview item then how can i get email address of that contact (on toast) .? – rushank shah Jun 02 '16 at 18:19
  • @rushankshah by using `adapter.getItem()` ? – pskink Jun 02 '16 at 18:22
  • yes by using adapter.getItem()..i have used `public void onItemClick(AdapterView> parent, View view, int position, long id) { String second = list.get(position).emails.toString(); }` but it gives me only 1st position record. – rushank shah Jun 02 '16 at 18:23
  • @rushankshah if you are clicking on the first item the `position` is 0, if you are clicking on the second item the `position` is 1 etc – pskink Jun 02 '16 at 18:27
  • I have bind the adapter with `AutoCompleteTextView` . so every time i am getting 0th position data. – rushank shah Jun 02 '16 at 18:30
  • @rushankshah what is the value of `position` in `onItemClick` method? – pskink Jun 02 '16 at 18:31
  • It shows 0 if i am selecting 1st name , shows 1st if i am selecting 2nd name. But actually the position of that name and email is 20th. – rushank shah Jun 02 '16 at 18:33
  • Have you tried this method with `AutoCompleteTextView` ? – rushank shah Jun 03 '16 at 05:03
  • @pskink..if u dont mind can u plz see this [question](http://stackoverflow.com/questions/39268248/access-email-id-along-with-name-and-phone-number-from-contact-list-in-android)..i am having trouble in getting email along with name and phone number. – sunil y Sep 02 '16 at 04:38
  • @sunily you have my code, so what is the problem with it? – pskink Sep 02 '16 at 04:43
  • yes..but i have to send to the php server..in my question i have explained my problem..can u please see that..?where i am going wrong..? – sunil y Sep 02 '16 at 04:48
  • @sunily so iterate over `List list` and send every item to your server: `for (AddressBookContact item : list) { ...` – pskink Sep 02 '16 at 04:49
  • @pskink.i have implemented like ur code..it is working fine..but can u pls explain how to send tot he php server..i mean how to convert that to JSONArray and send to server.. – sunil y Sep 02 '16 at 06:36
  • @pskink how to get those values separately..i mean email,number,name in separate Strings and how to create json array..? – sunil y Sep 02 '16 at 07:26
  • @pskink can you help me to get call logs faster and group and show count of sequential called number – Sagar Sep 14 '17 at 05:30
  • @pskink..if u don't mind can you please see this (https://stackoverflow.com/questions/46219231/how-to-load-call-logs-faster-like-other-apps-and-display-like-attached-image) question.I am having trouble in loading call log faster – Sagar Sep 15 '17 at 08:32
  • Wow, great, this actually uses a SINGLE query, which guarantees maximum performance.. This is the right answer, I can't believe how most of other answers use multiple queries for a simple use case like this.. – Marko Gajić Sep 19 '17 at 10:49
  • 1
    @MarkoGajić this is normal, most users blindly use ^C / ^V without any 5 minute reflection on how some things could be done better - the best example is hundreds of posts here on how to move / scale / rotate images - they use huge amount of spaghetti code while it can be done using [this](https://stackoverflow.com/a/21657145/2252830) simple `MatrixGestureDetector` – pskink Sep 20 '17 at 06:27
2

try this code ,use a progress dialouge

public void getAllContacts() {


    new AsyncTask<String, String, ArrayList<UserInfo>>() {

        ArrayList<UserInfo> infos = new ArrayList<>();

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected ArrayList<UserInfo> doInBackground(String... params) {

            ContentResolver contactResolver = context.getContentResolver();

            Cursor cursor = contactResolver.query(ContactsContract.Contacts.CONTENT_URI, new String[] { ContactsContract.Contacts._ID,
                    ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.HAS_PHONE_NUMBER }, null, null, null);

            if(cursor.getCount()>0)
            while ( cursor.moveToNext()) {

                String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
               // String photoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI));
                String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
              //  String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));

                Log.d("TAG",  " Name: " + displayName);

                if (Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0)
                {
                    Cursor pCur = contactResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                            null,
                            ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { contactId }, null);

                    while (pCur.moveToNext())
                    {
                        String phone = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                        String type = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
                        String s = (String) ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(), Integer.parseInt(type), "");

                        Log.d("TAG", s + " phone: " + phone);
                    }
                    pCur.close();
                }

                Cursor emailCursor = contactResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
                        null,
                        ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[] { contactId }, null);

                while (emailCursor.moveToNext())
                {
                    String phone = emailCursor.getString(emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
                    int type = emailCursor.getInt(emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
                    String s = (String) ContactsContract.CommonDataKinds.Email.getTypeLabel(context.getResources(), type, "");

                    Log.d("TAG", s + " email: " + phone);
                }

                emailCursor.close();

            }  cursor.close();

            return null;
        }

        @Override
        protected void onPostExecute(ArrayList<UserInfo> aVoid) {
            super.onPostExecute(aVoid);
            // EventBus.getDefault().post(aVoid);
        }
    }.execute();

}
Ameen Maheen
  • 2,719
  • 1
  • 28
  • 28
2

You retrieve all columns in your query:

Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
                    null, null, null, null);

This makes the data processing much slower. If you define an array of columns which you really need to retrieve, it will be much faster.

user2924714
  • 1,918
  • 1
  • 19
  • 26