get contacts performance issue

2019-05-01 17:32发布

问题:

I am trying to get contacts of the android device. I found a great sample code here

However, when trying to populate a ListActivity with ArrayAdapter that is using that code it is taking a lot of time - almost 4 seconds on galaxy s2 and much more on older devices. I need to improve that performance. What I thought is to implements Cursor that will hold more than one dimension Cursor, and use SimpleCursorAdapter as the list adapter.
I see few problems with this approach:

  • I don't know how to get item at specific position.
  • each cursor has different columns.

Is there a better/easier way to do that?

EDIT:

here is my code:

public List<ContactData> readContacts(){
    ContentResolver cr = getContentResolver();
    List<ContactData> cd = new ArrayList<ContactData>();
    Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
           null, null, null, ContactsContract.Contacts.DISPLAY_NAME);

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

               // get the phone number
               Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,
                                      ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
                                      new String[]{id}, null);
               while (pCur.moveToNext()) {
                     String phone = pCur.getString(
                            pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                     if (!Utils.isEmpty(phone)) {
                         cd.add(new ContactData(id, name, phone));
                     }
               }
               pCur.close();


           }
       }
  }
    return cd;
}

EDIT 2:

managed to fix it by changing it to only one query:

public List<ContactData> readContacts(){
    ContentResolver cr = getContentResolver();
    List<ContactData> cd = new ArrayList<ContactData>();
               Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                       new String[]{ContactsContract.CommonDataKinds.Phone._ID,ContactsContract.CommonDataKinds.Phone.NUMBER,ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY},
                       ContactsContract.CommonDataKinds.Phone.NUMBER + " IS NOT NULL",
                                      null, 
                                      ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY);
               while (pCur.moveToNext()) {
                     String phone = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                     String id = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID));
                     String name = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY));
                         ContactData cd1 = contactDataProvider.get();
                         cd1.id = id;
                         cd1.name = name;
                         cd1.phone = phone;
                         cd.add(cd1);
               }
               pCur.close();
    return cd;
}

回答1:

You can get name and ID at the same time you get the phone number:

String[] PROJECTION=new String[] {Contacts._ID, Contacts.DISPLAY_NAME, Phone.NUMBER};
Cursor pCur = cr.query(Phone.CONTENT_URI, PROJECTION, Phone.CONTACT_ID +" = ?",
                                      new String[]{id}, null);

This eliminates the per-row sub-query.



回答2:

Using a Cursor is a great approach for this problem. To get retrieve information from a specific position, you can use Cursor.moveToPosition(int position). Then you can access any field of the Cursor, in this case a String, using Cursor.getString(int columnIndex).

Take a look at the Cursor documentation for more details.

The best part about this approach is that you can implement a CursorLoader to do the background data retrieval for you. It also automatically handles repopulating your ListView on data changes, orientation changes, etc. Check out this great tutorial for more explanation.