Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android fetch all contact list (name, email, phone) takes more then a minute for about 700 contacts

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();
like image 328
itay83 Avatar asked Nov 07 '14 15:11

itay83


3 Answers

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);
    }
}
like image 170
pskink Avatar answered Nov 02 '22 23:11

pskink


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

}
like image 25
Ameen Maheen Avatar answered Nov 02 '22 23:11

Ameen Maheen


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.

like image 22
user2924714 Avatar answered Nov 03 '22 00:11

user2924714