In my app I'm fetching all contacts with cursor.
private static final String[] PROJECTION = new String[]{
PHONE_CONTACT_ID,
DISPLAY_NAME,
TIMESTAMP,
HAS_PHONE_NUMBER};
CursorLoader cursorLoader = new CursorLoader(ResUtils.getInstance().getContext(),
ContactsContract.Contacts.CONTENT_URI, PROJECTION, null, null,
"UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ")ASC");
Cursor cursor = cursorLoader.loadInBackground();
// Loop for every contact in the phone
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
ArrayList<String> phoneNumbers = new ArrayList<>();
String contact_id = cursor.getString(cursor.getColumnIndex(PHONE_CONTACT_ID));
String name = cursor.getString(cursor.getColumnIndex(DISPLAY_NAME));
String timeStamp = cursor.getString(cursor.getColumnIndex(TIMESTAMP));
cursorLoader = new CursorLoader( ResUtils.getInstance().getContext(), ContactsContract.CommonDataKinds.Email.CONTENT_URI,
new String[]{EMAIL},ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[]{contact_id}, null);
Cursor emailCur = cursorLoader.loadInBackground();
while (emailCur.moveToNext()) {
String email = emailCur.getString(emailCur.getColumnIndex(EMAIL));
if (TmlyUtils.isValidEmail(email)) {
phoneNumbers.add(ContactUtils.MAIL_TAG + email);
}
}
emailCur.close();
if (Integer.parseInt(cursor.getString(cursor.getColumnIndex(HAS_PHONE_NUMBER))) > 0) {
cursorLoader = new CursorLoader( ResUtils.getInstance().getContext(), ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{NUMBER},ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]{contact_id}, null);
Cursor phones = cursorLoader.loadInBackground();
while (phones.moveToNext()) {
String phoneNumber = phones.getString(phones.getColumnIndex(NUMBER));
phoneNumber = isValidMobileNumber(phoneNumber);
if (!phoneNumber.isEmpty() && !phoneNumbers.contains(ContactUtils.UPN_TAG + phoneNumber)) {
phoneNumbers.add(ContactUtils.UPN_TAG + phoneNumber);
}
}
phones.close();
}
}
cursor.close();
}
The code works fine but when you have thousands of contacts the app freeze during several seconds.
I'm using three cursor, the first one allow me to get all contacts in my phone
CursorLoader cursorLoader = new CursorLoader(ResUtils.getInstance().getContext(),
ContactsContract.Contacts.CONTENT_URI, PROJECTION, null, null,
"UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ")ASC");
Cursor cursor = cursorLoader.loadInBackground();
The second one loop for every email adress
cursorLoader = new CursorLoader( ResUtils.getInstance().getContext(), ContactsContract.CommonDataKinds.Email.CONTENT_URI,
new String[]{EMAIL},ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[]{contact_id}, null);
Cursor emailCur = cursorLoader.loadInBackground();
The third one loop for every phone
cursorLoader = new CursorLoader( ResUtils.getInstance().getContext(), ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{NUMBER},ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]{contact_id}, null);
Cursor phones = cursorLoader.loadInBackground();
I need to use the second and the third one If I'm not using it I can't get all phones and email for every contacts, because one contact can have several phones and email address
I try to use CursorLoader for speed up, but it's not sufficient, is it possible to get rid of the second and the third cursor ?
EDIT : I forgot to say that all the query is already inside a Retrofit call
This is a very common issue when working with Android ContactsContract APIs, you're basically doing one big query to get all contacts, and then a ton of little queries to get phones and emails.
So on a phone with 1000 contacts, you may have to do 2001 queries to get all info.
Instead, you can have one query to get all the info you need, you just need to skip the Contacts
table and query directly on the Data
table which contains all the info you need.
I didn't quite understand what you're doing with contact_id
, name
, and those MAIL_TAG
/UPN_TAG
's, so I'll just print to the log the info you'd need and you'll need to sort that into the java objects that make sense to your app.
String[] projection = {Data.CONTACT_ID, Data.DISPLAY_NAME, Data.MIMETYPE, Data.DATA1};
String selection = Data.MIMETYPE + " IN ('" + Phone.CONTENT_ITEM_TYPE + "', '" + Email.CONTENT_ITEM_TYPE + "')";
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
while (cur.moveToNext()) {
long id = cur.getLong(0); // contact-id
String name = cur.getString(1); // contact name
String mime = cur.getString(2); // type: email / phone
String data = cur.getString(3); // the actual info, e.g. +1-212-555-1234
switch (mime) {
case Phone.CONTENT_ITEM_TYPE:
Log.i("Contacts", "got a phone: " + id + ", " + name + ", " + data);
break;
case Email.CONTENT_ITEM_TYPE:
Log.i("Contacts", "got an email: " + id + ", " + name + ", " + data);
break;
}
}
cur.close();
Also, as @IliaKuzmin said, you should run this on a background thread, not the UI thread.