3

I want to retrieve >10000 contacts from the android device. To fetch that much contact it takes about 8-10 min. Is there any other possible way to do this. I have implemented a method its working fine but when it comes to large number of contacts it taking it time to fetch the contacts.

 ContentResolver cr = getActivity().getApplication().getContentResolver();
    Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
    if (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 (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);
                while (pCur.moveToNext()) {
                    int phoneType = pCur.getInt(pCur.getColumnIndex(
                            ContactsContract.CommonDataKinds.Phone.TYPE));
                    String phoneNumber = pCur.getString(pCur.getColumnIndex(
                            ContactsContract.CommonDataKinds.Phone.NUMBER));

                    phoneNumber = phoneNumber.replace(" ","");
                    phoneNumber = phoneNumber.replace("-","");

                    boolean addNumber = stringCheck(phoneNumber,symbols);

                    if (!addNumber){

                        if (phoneNumber.length() == 10){
                            addContact(phoneNumber,phoneType,name);
                        }else if (phoneNumber.length() == 11){
                            phoneNumber = phoneNumber.substring(1);
                            addContact(phoneNumber,phoneType,name);

                        }else if (phoneNumber.length() == 12){
                            phoneNumber = phoneNumber.substring(2);
                            addContact(phoneNumber,phoneType,name);

                        }else if (phoneNumber.length() == 13){
                            phoneNumber = phoneNumber.substring(3);
                            addContact(phoneNumber,phoneType,name);
                        }

                    }

                }
                pCur.close();
            }
        }
    }
  • 1
    hint : fetch required things only instead of everything, it will reduce query execution time. – Ravi Dec 21 '16 at 05:54
  • I need all the numbers associated with a contact(Like his mobile,office,home). For that the above ones are required right? – Sunil Subramannian Dec 21 '16 at 06:04
  • Are you using all the contacts to show in a list or for data storage? Because it depends if you are using the contacts for showing in list then you can use load more for fetching contacts with a bunch of 500 contacts at a time. – Ready Android Dec 21 '16 at 06:06
  • 1
    Here i can see you are using `_ID` , `DISPLAY_NAME` and `HAS_PHONE_NUMBER` only from `cur`, so query for those fields only. see [reference](http://stackoverflow.com/questions/6587674/android-contacts-display-name-and-phone-numbers-in-single-database-query) – Ravi Dec 21 '16 at 06:07
  • First, as @RaviRupareliya suggests, you should only query for columns you actually need. Second, You haven't necessarily identified the source of slowness. There are other method calls and it's unclear what performance impact they have. If you want more specific advice on how to make your code more performant, you should use some of the profiling tools in Android Studio to measure where the slowdown is occurring. – Karakuri Dec 21 '16 at 06:19
  • @ReadyAndroid I want to show it in list but modifications are there, so I have to first provide it to the server...So am storing it in an arraylist from there I'm sending it to server. – Sunil Subramannian Dec 21 '16 at 06:46
  • check this example you may get some optimisation https://developer.android.com/training/contacts-provider/retrieve-names.html – Nas Dec 21 '16 at 06:48
  • @RaviRupareliya Ok,Let me check that way.. – Sunil Subramannian Dec 21 '16 at 06:49
  • @Karakuri As you said, I will check with the profiling tools..Will inform you the details Once I go through it. – Sunil Subramannian Dec 21 '16 at 06:50
  • @Nas I went through before that one didn't help. – Sunil Subramannian Dec 21 '16 at 06:52
  • @Sunil, So in this case you should optimize your code. As per your code, you have two cursors with more conditions check. Do all these checks at server side. In android code only fetch for display name and it's respective phone number and send to server. – Ready Android Dec 21 '16 at 06:54

4 Answers4

0

Keep your fetching process in doInBackground method of AyncTask and then display it. And get only those info which is required first, eg, Contact Name and ID.

refer this ans for more clarification: Fetching a large number of contacts

Community
  • 1
  • 1
Sanat Pandey
  • 4,081
  • 17
  • 75
  • 132
  • In my case I want all the contacts in order to move forward. So I cant load them in background. I want to fetch contacts and send to server and the response I want to show in a list. – Sunil Subramannian Dec 21 '16 at 06:58
0

In order to fetch large number of contacts you must be fetching the contacts in the form of pages by using rest API. For example page size of contacts is 100.

One possible approach:

If you are showing the contacts in the listview then you can fetch the data while user is scrolling the list. For that You have to set the threshold position of the list i.e. 80, So when user reaches at 80th position while scrolling you can hit your rest API for next page to fetch the new contacts and add those contacts in the listview.

From your comments i can see you want all the contacts in order to move forward.For this you can start an intent service when app is launched. This service will fetch all the contacts and store them. You can use them as per your needs

Awadesh
  • 3,530
  • 2
  • 20
  • 32
  • I went through this case also. But when I want to search for a particular contact this method wont help. If so, I have to implement search from server and the procedure will get long. – Sunil Subramannian Dec 21 '16 at 06:56
  • @Sunil Subramannian Ok for that what you can do is .. You can start an intent service when app is launched. This service will fetch all the contacts and store them. You can use them as per your needs – Awadesh Dec 21 '16 at 07:00
0

Here some optimization for speed up

String[] select = new String[]{ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.HAS_PHONE_NUMBER};

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

String[] selectOnly = new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.TYPE};

Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        selectOnly,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
                        new String[]{id}, null);

Do your number check, length check in server side if possible. Do it in Async task.

Nas
  • 2,158
  • 1
  • 21
  • 38
0

If 900 out of the 1000 contacts have phone numbers, you're currently querying the DB 901 times, you can reduce it to only two queries:

  1. Give me all contacts information
  2. Give me all phone numbers

Then you use the contact-id field on both to match the phone to the right contacts.

Also, as noted in other answers, you really should add projection to all your queries to improve performance.

Another improvement you can make is to avoid runtime cur.getColumnIndex() calls, if you have a projection, you should already know the index - so use it hard-coded

Map<Long, List<String>> phones = new HashMap<>();
ContentResolver cr = getActivity().getApplication().getContentResolver();

// First build a mapping: contact-id > list of phones
Cursor cur = cr.query(Phone.CONTENT_URI, new String[] { Phone.CONTACT_ID, Phone.Number }, null, null, null);
while (cur != null && cur.moveToNext()) {
   long contactId = cur.getLong(0);
   String phone = cur.getString(1);
   List list;
   if (phones.contains(contactId)) {
      list = phones.get(contactId);
   } else {
      list = new ArrayList<String>();
      phones.put(contactId, list);
   }
   list.add(phone);
}
cur.close();

// Next query for all contacts, and use the phones mapping
cur = cr.query(Contacts.CONTENT_URI, new String[] { Contacts._ID, Contacts.DISPLAY_NAME }, null, null, null);
while (cur != null && cur.moveToNext()) {
   long id = cur.getLong(0);
   String name = cur.getString(1);
   List<String> contactPhones = phones.get(id);
   addContact(id, name, contactPhones);
}
marmor
  • 27,641
  • 11
  • 107
  • 150