Android联系人号码重复问题:原因分析与解决方法
Android联系人列表重复号码问题分析与解决
问题背景
开发Android应用时,需展示联系人列表及对应电话号码。通过ContentProvider分别获取联系人数据与所有号码,再通过contactId关联后,发现多个联系人存在重复号码(同一号码出现2-4次),添加测试联系人后该问题依然存在。
问题原因
- 多存储源重复条目:
ContactsContract.CommonDataKinds.Phone会返回同一联系人来自不同存储介质(如Google账户、SIM卡、本地存储)的同号码记录——这些属于不同的RawContact,但会关联到同一个ContactId,导致查询时出现重复。 - 号码格式未归一化:同一号码可能存在不同格式(如带
+86前缀、含空格/括号、纯数字),未统一格式就直接添加,会被识别为不同号码导致重复。 - 代码未做去重处理:当前
retrieveAllContactNumbers方法直接将查询到的所有号码存入Map,未对同一contactId下的重复号码做去重逻辑。
解决办法
方案1:查询阶段去重
在查询号码时,通过DISTINCT关键字过滤重复条目,同时指定查询NORMALIZED_NUMBER字段(系统已归一化的号码),避免格式差异导致的重复:
fun Context.retrieveAllContactNumbers(): HashMap<String, ArrayList<String>> { val contactsNumberMap = HashMap<String, ArrayList<String>>() // 指定查询字段,只取需要的列,提升性能 val projection = arrayOf( ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER ) val phoneCursor = contentResolver.query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null, // 添加DISTINCT去重 "DISTINCT" ) phoneCursor?.use { cursor -> val contactIdIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID) val normalizedNumberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER) while (cursor.moveToNext()) { val contactId = cursor.getString(contactIdIndex) val number = cursor.getString(normalizedNumberIndex) ?: continue // 去重后添加 if (contactsNumberMap.containsKey(contactId)) { val numbers = contactsNumberMap[contactId] if (!numbers?.contains(number)!!) { numbers.add(number) } } else { contactsNumberMap[contactId] = arrayListOf(number) } } } return contactsNumberMap }
方案2:关联阶段统一格式并去重
在关联联系人与号码时,先对号码做归一化处理,再添加到联系人列表前做去重:
contacts.forEach { contactData -> contactNumbers[contactData.contactId]?.let { numbers -> val uniqueNumbers = mutableSetOf<String>() numbers.forEach { rawNumber -> val normalizedNumber = PhoneNumberUtils.normalizeNumber(rawNumber) if (normalizedNumber != null && isValidIrisCellphoneNumber(normalizedNumber)) { uniqueNumbers.add(normalizedNumber) } } contactData.phoneNumbers.addAll(uniqueNumbers) } }
方案3:优化联系人查询逻辑
可以合并联系人与号码的查询,一次性获取联系人基本信息和号码,减少IO操作同时避免重复关联:
fun Context.retrieveContactsWithNumbers(): List<ContactData> { val result = mutableListOf<ContactData>() val projection = arrayOf( ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY, ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER ) contentResolver.query( ContactsContract.Data.CONTENT_URI, projection, "${ContactsContract.Data.MIMETYPE} = ?", arrayOf(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE), "${ContactsContract.Contacts.DISPLAY_NAME_PRIMARY} ASC" )?.use { cursor -> val contactIdIndex = cursor.getColumnIndex(ContactsContract.Contacts._ID) val nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY) val numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER) val contactMap = mutableMapOf<String, ContactData>() while (cursor.moveToNext()) { val contactId = cursor.getString(contactIdIndex) val name = cursor.getString(nameIndex) ?: "" val number = cursor.getString(numberIndex) ?: continue val contact = contactMap.getOrPut(contactId) { val firstName = name.split(" ").firstOrNull() ?: "" ContactData(contactId, firstName, getInitials(firstName, ""), mutableListOf(), null) } if (isValidIrisCellphoneNumber(number) && !contact.phoneNumbers.contains(number)) { contact.phoneNumbers.add(number) } } result.addAll(contactMap.values) } return result }
内容的提问来源于stack exchange,提问作者james04
相关产品推荐
相关产品推荐

