How load all the contacts with minimum time in Android
Asked Answered
T

5

23

In my project getting contacts is taking a long time to load.

  • What are ways to reduce the time of getting contacts
  • Assume there are 1000 contacts in my phone.
  • Right now it is taking more than 2 minutes to load all the contacts

How can I reduce the time to load contacts ? Any Thoughts?

I referred to the the following link when programming the initial method.

http://www.coderzheaven.com/2011/06/13/get-all-details-from-contacts-in-android/

Tether answered 1/6, 2012 at 5:2 Comment(0)
P
10

Total time will depend upon what fields you are trying to access from the Contacts table. Accessing less field means less looping , less processing and hence faster results.

Also to speed up your contacts fetch operation you can use the ContentProvideClient instead of calling query on ContentResolver every time. This will make you query the specific table rather than querying first for the required ContentProvider and then to table.

Create an instance of ContentProviderClient

ContentResolver cResolver=context.getContextResolver();
ContentProviderClient mCProviderClient = cResolver.acquireContentProviderClient(ContactsContract.Contacts.CONTENT_URI);

Then reuse this mCProviderClient to get Contacts(data from any ContentProvider) data on your call. For example in following method, I am accessing only one field.

private ArrayList<String> fetchContactsCProviderClient()
    {
        ArrayList<String> mContactList = null;
        try
        {
            Cursor mCursor = mCProviderClient.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
            if (mCursor != null && mCursor.getCount() > 0)
            {
                mContactList = new ArrayList<String>();
                mCursor.moveToFirst();
                while (!mCursor.isLast())
                {
                    String displayName = mCursor.getString(mCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
                    mContactList.add(displayName);
                    mCursor.moveToNext();
                }
                if (mCursor.isLast())
                {
                    String displayName = mCursor.getString(mCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
                    mContactList.add(displayName);
                }
            }

            mCursor.close();
        }
        catch (RemoteException e)
        {
            e.printStackTrace();
            mContactList = null;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            mContactList = null;
        }

        return mContactList;
    }
Pokeweed answered 13/7, 2012 at 7:56 Comment(0)
H
32

BETTER SOLUTION HERE.....

private static final String[] PROJECTION = new String[] {
        ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
        ContactsContract.Contacts.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Phone.NUMBER
    };
.
.
.

ContentResolver cr = getContentResolver();
        Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null);
        if (cursor != null) {
            try {
                final int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
                final int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);

                String name, number;
                while (cursor.moveToNext()) {
                    name = cursor.getString(nameIndex);
                    number = cursor.getString(numberIndex);
                }
            } finally {
                cursor.close();
            }
        }

CHEERS...:)

Heathenism answered 24/2, 2015 at 7:13 Comment(8)
this is a smart solution, but what if the contact has multiple phone numbers ?Aubry
@Melbourne Lopes- you only reading only phone number, how can I read email id along with that.Groceryman
Why is this a better solution?Taft
Because of optimized projection on query it gives results extremely fast :)Heathenism
really fast.. 400 contacts within second.. should be accepted answer thisOech
@miladzahedi check above answer I have uploaded.Kafka
This approach only returns phone numbers that have country codes appended to them.Gomuti
great!! fetches about 500 contacts within milliseconds.Ecology
P
10

Total time will depend upon what fields you are trying to access from the Contacts table. Accessing less field means less looping , less processing and hence faster results.

Also to speed up your contacts fetch operation you can use the ContentProvideClient instead of calling query on ContentResolver every time. This will make you query the specific table rather than querying first for the required ContentProvider and then to table.

Create an instance of ContentProviderClient

ContentResolver cResolver=context.getContextResolver();
ContentProviderClient mCProviderClient = cResolver.acquireContentProviderClient(ContactsContract.Contacts.CONTENT_URI);

Then reuse this mCProviderClient to get Contacts(data from any ContentProvider) data on your call. For example in following method, I am accessing only one field.

private ArrayList<String> fetchContactsCProviderClient()
    {
        ArrayList<String> mContactList = null;
        try
        {
            Cursor mCursor = mCProviderClient.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
            if (mCursor != null && mCursor.getCount() > 0)
            {
                mContactList = new ArrayList<String>();
                mCursor.moveToFirst();
                while (!mCursor.isLast())
                {
                    String displayName = mCursor.getString(mCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
                    mContactList.add(displayName);
                    mCursor.moveToNext();
                }
                if (mCursor.isLast())
                {
                    String displayName = mCursor.getString(mCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
                    mContactList.add(displayName);
                }
            }

            mCursor.close();
        }
        catch (RemoteException e)
        {
            e.printStackTrace();
            mContactList = null;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            mContactList = null;
        }

        return mContactList;
    }
Pokeweed answered 13/7, 2012 at 7:56 Comment(0)
K
8

Load Contact faster like other apps doing.

I have tested this code with multiple contacts its working fine and faster like other apps within 500 ms (within half second or less) I am able to load 1000+ contacts.

Total time will depend upon what fields you are trying to access from the Contacts table.

Mange your query according to your requirement do not access unwanted fields. Accessing less field means less looping , less processing and hence faster results.

Accessing right table in contact it also help to reduce contact loading time.

Query Optimization to load contact more faster use projection

 String[] projection = {
            ContactsContract.Data.MIMETYPE,
            ContactsContract.Data.CONTACT_ID,
            ContactsContract.Contacts.DISPLAY_NAME,
            ContactsContract.Contacts.PHOTO_URI,
            ContactsContract.Contacts.STARRED,
            ContactsContract.RawContacts.ACCOUNT_TYPE,
            ContactsContract.CommonDataKinds.Contactables.DATA,
            ContactsContract.CommonDataKinds.Contactables.TYPE
    };

Selection and selection argument

String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)" + " AND " /*+ ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" + 1 + "' AND "*/ +
            ContactsContract.Data.HAS_PHONE_NUMBER + " = '" + 1 + "'";

    String[] selectionArgs = {
            ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
            ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
    };

To order contacts alphabetically use following code

 try {
        Collections.sort(listview_address, new Comparator<ContactBook>() {
            @Override
            public int compare(ContactBook lhs, ContactBook rhs) {
                return lhs.name.toUpperCase().compareTo(rhs.name.toUpperCase());
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }

Following is complete source code

 public void initeContacts() {
    List<ContactBook> listview_address = new LinkedList<ContactBook>();
    SparseArray<ContactBook> addressbook_array = null;
    {
        addressbook_array = new SparseArray<ContactBook>();

        long start = System.currentTimeMillis();

        String[] projection = {
                ContactsContract.Data.MIMETYPE,
                ContactsContract.Data.CONTACT_ID,
                ContactsContract.Contacts.DISPLAY_NAME,
                ContactsContract.Contacts.PHOTO_URI,
                ContactsContract.Contacts.STARRED,
                ContactsContract.RawContacts.ACCOUNT_TYPE,
                ContactsContract.CommonDataKinds.Contactables.DATA,
                ContactsContract.CommonDataKinds.Contactables.TYPE
        };

        String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)" + " AND " /*+ ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" + 1 + "' AND "*/ +
                ContactsContract.Data.HAS_PHONE_NUMBER + " = '" + 1 + "'";

        String[] selectionArgs = {
                ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
                ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
        };

        String sortOrder = ContactsContract.Contacts.SORT_KEY_ALTERNATIVE;

        Uri uri = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
            uri = ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
        } else {
            uri = ContactsContract.Data.CONTENT_URI;

        }
        // we could also use Uri uri = ContactsContract.Data.CONTENT_URI;
        // we could also use Uri uri = ContactsContract.Contact.CONTENT_URI;

        Cursor cursor = getActivity().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 photo = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.PHOTO_URI);
        final int typeIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.TYPE);
        final int account_type = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE);
        while (cursor.moveToNext()) {
            int contact_id = cursor.getInt(idIdx);
            String photo_uri = cursor.getString(photo);
            String contact_name = cursor.getString(nameIdx);
            String contact_acc_type = cursor.getString(account_type);
            int contact_type = cursor.getInt(typeIdx);
            String contact_data = cursor.getString(dataIdx);
            ContactBook contactBook = addressbook_array.get(contact_id);

           /* if (contactBook == null) {
                //list  contact add to avoid duplication
                //load All contacts fro device
                //to add contacts number with name add one extra veriable in ContactBook as number and pass contact_data this give number to you (contact_data is PHONE NUMBER)
                contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "phone number");
                addressbook_array.put(contact_id, contactBook);
                listview_address.add(contactBook);

            }*/

            String Contact_mimeType = cursor.getString(mimeTypeIdx);
            //here am checking Contact_mimeType to get mobile number asociated with perticular contact and email adderess asociated
            if (Contact_mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {

                if (contactBook != null) {
                    contactBook.addEmail(contact_type, contact_data);
                }

            } else {
                if (contactBook == null) {
                    //list  contact add to avoid duplication
                    //load All contacts fro device
                    //to add contacts number with name add one extra veriable in ContactBook as number and pass contact_data this give number to you (contact_data is PHONE NUMBER)
                    contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "phone number");
                    addressbook_array.put(contact_id, contactBook);
                    listview_address.add(contactBook);

                }
               // contactBook.addPhone(contact_type, contact_data);


            }


        }

        cursor.close();


        try {
            Collections.sort(listview_address, new Comparator<ContactBook>() {
                @Override
                public int compare(ContactBook lhs, ContactBook rhs) {
                    return lhs.name.toUpperCase().compareTo(rhs.name.toUpperCase());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

You can use following code in above code that I have commented .It club the the single contact with its multiple number.To get all number associated with single contact use array in Object class.

 if (contactBook == null) {
            //irst  contact add to avoid duplication
            //load All contacts fro device
            contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "");
            addressbook_array.put(contact_id, contactBook);
            listview_address.add(contactBook);
        }

        String Contact_mimeType = cursor.getString(mimeTypeIdx);
        //here am checking Contact_mimeType to get mobile number asociated with perticular contact and email adderess asociated
        if (Contact_mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {

            contactBook.addEmail(contact_type, contact_data);

        } else {

            contactBook.addPhone(contact_type, contact_data);

        }

Object class

public class ContactBook {
public int id;
public Resources res;
public String name;
public String photo;
public String contact_acc_type;
public SparseArray<String> emails;
public SparseArray<String> phones;
/*  public LongSparseArray<String> emails;
public LongSparseArray<String> phones;*/
public String header = "";


public ContactBook(int id, String name, Resources res, String photo, String contact_acc_type, String header) {
    this.id = id;
    this.name = name;
    this.res = res;
    this.photo = photo;
    this.contact_acc_type = contact_acc_type;
    this.header = header;
}

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

public String toString(boolean rich) {

    //testing method to check ddata
    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) {
    //this is the array in object class where i am storing contact all emails of perticular contact (single)
    if (emails == null) {
        //   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        emails = new SparseArray<String>();
        emails.put(type, address);
        /*} else {
            //add emails to array below Jelly bean //use single array list
        }*/
    }
}

public void addPhone(int type, String number) {
    //this is the array in object class where i am storing contact numbers of perticular contact
    if (phones == null) {
        //  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        phones = new SparseArray<String>();
        phones.put(type, number);
       /* } else {
            //add emails to array below Jelly bean //use single array list
        }*/
    }
}}
Kafka answered 29/9, 2017 at 13:12 Comment(0)
C
5

If your time increases with your data, then you are probably running a new query to fetch phones/emails for every contact. If you query for the phone/email field using ContactsContract.CommonDataKinds.Phone.NUMBER, then you will just retrieve 1 phone per contact. The solution is to project the fields and join them by contact id.

Here is my solution in Kotlin (extracting id, name, all phones and emails):

val projection = arrayOf(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.CONTACT_ID,
        ContactsContract.Contacts.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Contactables.DATA

)
val selection = "${ContactsContract.Data.MIMETYPE} in (?, ?)"

val selectionArgs = arrayOf(
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

val contacts = applicationContext
        .contentResolver
        .query(ContactsContract.Data.CONTENT_URI, projection, selection, selectionArgs, null)
        .run {
            if (this == null) {
                throw IllegalStateException("Cursor null")
            }
            val contactsById = mutableMapOf<String, LocalContact>()
            val mimeTypeField = getColumnIndex(ContactsContract.Data.MIMETYPE)
            val idField = getColumnIndex(ContactsContract.Data.CONTACT_ID)
            val nameField = getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
            val dataField = getColumnIndex(ContactsContract.CommonDataKinds.Contactables.DATA)

            while (moveToNext()) {
                val mimeType = getString(mimeTypeField)
                val id = getString(idField)
                var contact = contactsById[id]
                if (contact == null) {
                    val name = getString(nameField)
                    contact = LocalContact(id = id, fullName = name, phoneNumbers = listOf(), emailAddresses = listOf())
                }
                val data = getString(dataField)
                when(getString(mimeTypeField)) {
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ->
                        contact = contact.copy(emailAddresses = contact.emailAddresses + data)
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ->
                        contact = contact.copy(phoneNumbers = contact.phoneNumbers + data)
                }
                contactsById[id] = contact
            }
            close()
            contactsById.values.toList()
        }

And for reference, my LocalContact model:

data class LocalContact(
        val id: String,
        val fullName: String?,
        val phoneNumbers: List<String>,
        val emailAddresses: List<String>
)
Chalcidice answered 19/3, 2020 at 13:28 Comment(1)
This it the only answers that solves both performance issues and missing phone numbers. As well as listing both all emails and all phone numbers for a contact in one query. Kudos to you mate.Peritoneum
H
0

I think this is a better solution:

public ContentValues getAllContacts() {
    ContentValues contacts = new ContentValues();
    ContentResolver cr = getContentResolver();
    Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

    if (cur != null && cur.getCount() > 0) {
        while (cur.moveToNext()) {
            String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
            String name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

            if (cur.getInt(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);
                if (pCur != null) {
                    while (pCur.moveToNext()) {
                        String phoneNo = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                        contacts.put(phoneNo, name);
                    }
                    pCur.close();
                }
            }
        }
        cur.close();
    }
    return contacts;
}

for use it you need to call this lines once:

ContentValues contacts = new ContentValues();
contacts = getAllContacts();

and when you want to get contact name by number, just use:

String number = "12345";
String name = (String) G.contacts.get(number);

this algorithm is a bit faster...

Humic answered 29/7, 2017 at 4:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.