Search code examples
androidandroid-contentprovidercontactsandroid-contacts

Contact book read time on Android


I have written the following query to read contacts from a device.

private fun getContactPhoneNumbers(resolver: ContentResolver): Map<String, NameAndPhoneList> {
        val startTime = System.nanoTime()
        val map = hashMapOf<String, NameAndPhoneList>()
        val projection = arrayOf(
            ContactsContract.Contacts._ID,
            ContactsContract.Contacts.DISPLAY_NAME,
            ContactsContract.Contacts.HAS_PHONE_NUMBER,
            ContactsContract.Contacts.PHOTO_THUMBNAIL_URI
        )
        val selection =
            "${ContactsContract.Contacts.DISPLAY_NAME} NOT LIKE '' and ${ContactsContract.Contacts.DISPLAY_NAME} NOT NULL"
        resolver.query(
            ContactsContract.Contacts.CONTENT_URI,
            projection,
            selection,
            null,
            null,
            null
        )?.let { cursor ->
            if (cursor.count > 0) {
                while (cursor.moveToNext()) {
                    val id: String? =
                        cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
                    val name: String? =
                        cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                    val photoUri =
                        cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI))
                    val phone =
                        if (cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0) {
                            val pCur: Cursor = resolver.query(
                                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                                null,
                                ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
                                arrayOf(id),
                                null
                            )!!
                            val numbers = mutableListOf<String>()
                            while (pCur.moveToNext()) {
                                val phoneNo: String = pCur.getString(
                                    pCur.getColumnIndex(
                                        ContactsContract.CommonDataKinds.Phone.NUMBER
                                    )
                                )
                                numbers.add(phoneNo)
                            }
                            pCur.close()
                            numbers
                        } else
                            null
                    // take contacts which either have email or phone numbers
                    if (id != null && name != null) {
                        map[id] = NameAndPhoneList(name, phone, photoUri?.let { Uri.parse(it) })
                    }
                }
            }
            cursor.close()
        }
        val endTime = System.nanoTime() - startTime
        Timber.i("$CONTACT_SYNC_PHONE_MAP_QUERY_TIME = $endTime")
        return map
    }

    private data class NameAndPhoneList(
        val name: String,
        val phoneList: List<String>?,
        val imageUri: Uri?
    )

this is taking 112877872699 ns (~2 min) for a phonebook of length 6,300 contacts. Is this expected or can we optimize further?


Solution

  • for 6,300 contacts (assuming all have a phone) you're making 6,301 queries, which is why it's so slow...

    Instead you can benefit from ContactsContract's "implicit join" feature, which allows you to get Contacts.CONTENT_URI fields when querying over the Data.CONTENT_URI table.

    So, just query directly over the Data table, get all the phones including the CONTACT_ID, DISPLAY_NAME, etc. fields, and put it in some map contact-id => data.

    Here's sample code in Java that can help - https://stackoverflow.com/a/44383937/819355