Better way to get contact details via content provider in Android

Ravi Kumar Saini
AndroidTerest
Published in
2 min readDec 26, 2019

Generally, to get the mobile numbers, name and email address, you need to make a different query to a content resolver in Android. You get data from different queries; merge all those together to put the data in a single collection and show it on UI.

Multiple queries are performance hit as DB query involves connect, query and disconnect so fewer the queries, better the performance.

Old Approach :

  1. Get the list of contacts to contactCursor from Contacts.CONTENT_URI
  2. Get all phone numbers to phoneCursor from Phone.CONTENT_ITEM_TYPE
  3. Get all emails to emailCursor from Email.CONTENT_ITEM_TYPE
  4. Link: https://gist.github.com/srayhunter/47ab2816b01f0b00b79150150feb2eb2

New, Better and Optimized Approach :

I used the Rx Java approach to fetch the contact details on the separate observable.

Declare variables and constants

private const val DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
private val PROJECTION = arrayOf(
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.DATA1,
ContactsContract.Data.MIMETYPE
)
private const val ORDER = DISPLAY_NAME
private const val selection = ContactsContract.Data.MIMETYPE + " = ?" +
" OR " + ContactsContract.Data.MIMETYPE + " = ?" + " OR " + ContactsContract.Data.MIMETYPE + " = ?"
private val selectionArgs = arrayOf(
"%" + "@" + "%",
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)

and call the below code on separate observable or on a separate thread

val userContactDetailLinkedHashMap: LinkedHashMap<Int, UserContactDetail?> = LinkedHashMap()
try {
val cr: ContentResolver = context.getContentResolver()
val cur: Cursor = cr.query(
ContactsContract.Data.CONTENT_URI,
PROJECTION,
selection,
selectionArgs,
ORDER
)
if (cur != null && cur.getCount()>0 && cur.moveToFirst()) {
do {
val hasPhone: Int =
cur.getInt(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))
val contactId: Int =
cur.getInt(cur.getColumnIndex(ContactsContract.Data.CONTACT_ID))
val name: String =
cur.getString(cur.getColumnIndex(ContactsContract.Data.DISPLAY_NAME_PRIMARY))
val emailOrMobile: String? =
cur.getString(cur.getColumnIndex(ContactsContract.Data.DATA1))
if (hasPhone > 0 && emailOrMobile != null) {
var contactDetail: UserContactDetail?
if (!userContactDetailLinkedHashMap.containsKey(contactId)) {
contactDetail = UserContactDetail()
if (!CommonUtil.isEmailValid(emailOrMobile)) {
contactDetail.setPhoneNumber(Collections.singletonList(emailOrMobile))
} else {
contactDetail.setEmailId(Collections.singletonList(emailOrMobile))
}
contactDetail.setName(name)
userContactDetailLinkedHashMap[contactId] = contactDetail
} else {
contactDetail = userContactDetailLinkedHashMap[contactId]
if (contactDetail == null) continue
if (!CommonUtil.isEmailValid(emailOrMobile)) {
contactDetail.setPhoneNumber(Collections.singletonList(emailOrMobile))
} else {
contactDetail.setEmailId(Collections.singletonList(emailOrMobile))
}
contactDetail.setName(name)
userContactDetailLinkedHashMap[contactId] = contactDetail
}
}
} while (cur.moveToNext())
}
if (cur != null) {
cur.close()
}
} catch (e: Exception) {
Crashlytics.getInstance().core.logException(e)
}

Here the UserContactDetail have

class UserContactDetail {
var name: String? = null
var emailId: List<String> = ArrayList()
var phoneNumber: List<String> = ArrayList()
}

That's it. LinkedHashMap userContactDetailLinkedHashMap will have the final user details along with name, emails and mobile numbers.

--

--