Android Content Provider查询手机号无法匹配带区号号码的解决方法
解决通讯录号码格式不匹配的问题
问题核心在于通讯录中存储的号码格式多样(带+号、空格、分隔符等),而输入的是纯数字号码,直接用原始字段匹配会失败。通过号码规范化统一格式后再匹配,是最有效的解决思路,以下分版本提供具体实现:
方案1:API 21及以上(推荐)
利用系统提供的Phone.NORMALIZED_NUMBER字段(系统预先规范化的E.164标准号码),直接匹配输入的规范化号码:
修改ContactRepositoryImpl.kt代码:
class ContactRepositoryImpl @Inject constructor( @ApplicationContext private val context: Context ) : ContactRepository { override suspend fun getContacts(mobileNumbers: List<String?>): Result<List<Contact>> { val contactList = mutableListOf<Contact>() val validNumbers = mobileNumbers.filterNotNull().filter { it.isNotBlank() } if (validNumbers.isEmpty()) return Result.success(emptyList()) return runCatching { val contentResolver = context.contentResolver // 获取设备默认国家码(用于号码规范化,如孟加拉国为BD) val countryIso = getDefaultCountryIso() // 将输入号码转换为E.164标准格式 val normalizedInputNumbers = validNumbers.mapNotNull { rawNumber -> PhoneNumberUtils.formatNumberToE164(rawNumber, countryIso) } if (normalizedInputNumbers.isEmpty()) return@runCatching emptyList() val uri = Phone.CONTENT_URI val projection = arrayOf( Phone.NUMBER, Phone.DISPLAY_NAME, Phone.PHOTO_URI, Phone.NORMALIZED_NUMBER // 加入系统规范化号码字段 ) // 用规范化号码作为查询条件 val selection = "${Phone.NORMALIZED_NUMBER} IN (${normalizedInputNumbers.joinToString(",") { "?" }})" val selectionArgs = normalizedInputNumbers.toTypedArray() val cursor = contentResolver.query(uri, projection, selection, selectionArgs, null) cursor?.use { val phoneNumberIndex = it.getColumnIndex(Phone.NUMBER) val nameIndex = it.getColumnIndex(Phone.DISPLAY_NAME) val photoUriIndex = it.getColumnIndex(Phone.PHOTO_URI) while (it.moveToNext()) { val number = it.getString(phoneNumberIndex) ?: "" val name = it.getString(nameIndex) ?: "" val photoUri = it.getString(photoUriIndex) contactList.add( Contact( name = name, number = number, photoUri = photoUri ) ) } } contactList } } // 获取设备默认国家码 private fun getDefaultCountryIso(): String { val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager return telephonyManager.networkCountryIso.uppercase() } }
方案2:兼容API 21以下
如果需要支持更低版本,可先查询所有候选号码,再用PhoneNumberUtils.compare()在内存中判断匹配(该方法自动忽略格式、前缀差异):
class ContactRepositoryImpl @Inject constructor( @ApplicationContext private val context: Context ) : ContactRepository { override suspend fun getContacts(mobileNumbers: List<String?>): Result<List<Contact>> { val contactList = mutableListOf<Contact>() val validNumbers = mobileNumbers.filterNotNull().filter { it.isNotBlank() } if (validNumbers.isEmpty()) return Result.success(emptyList()) return runCatching { val contentResolver = context.contentResolver val uri = Phone.CONTENT_URI val projection = arrayOf( Phone.NUMBER, Phone.DISPLAY_NAME, Phone.PHOTO_URI ) // 先过滤出包含输入号码数字的记录,减少内存处理量 val cleanInputNumbers = validNumbers.map { it.replace(Regex("\\D"), "") }.joinToString("%") val selection = "${Phone.NUMBER} LIKE ?" val selectionArgs = arrayOf("%$cleanInputNumbers%") val cursor = contentResolver.query(uri, projection, selection, selectionArgs, null) cursor?.use { val phoneNumberIndex = it.getColumnIndex(Phone.NUMBER) val nameIndex = it.getColumnIndex(Phone.DISPLAY_NAME) val photoUriIndex = it.getColumnIndex(Phone.PHOTO_URI) while (it.moveToNext()) { val contactNumber = it.getString(phoneNumberIndex) ?: "" val name = it.getString(nameIndex) ?: "" val photoUri = it.getString(photoUriIndex) // 判断当前通讯录号码是否匹配任意输入号码 validNumbers.any { inputNumber -> PhoneNumberUtils.compare(inputNumber, contactNumber) }.takeIf { it }?.let { contactList.add( Contact( name = name, number = contactNumber, photoUri = photoUri ) ) } } } // 去重(同一联系人可能有多个匹配号码) contactList.distinctBy { it.number } } } }
关键说明
PhoneNumberUtils.formatNumberToE164():将号码转换为标准E.164格式(如+8801866985338),需传入对应国家码确保转换准确。Phone.NORMALIZED_NUMBER:API 21+专属字段,系统自动维护的规范化号码,避免手动处理格式差异。PhoneNumberUtils.compare():跨版本兼容,自动忽略格式、区号、分隔符等差异,判断两个号码是否指向同一联系人。- 权限要求:确保
AndroidManifest.xml中已添加<uses-permission android:name="android.permission.READ_CONTACTS"/>,并在运行时动态申请权限。
内容的提问来源于stack exchange,提问作者Aminul Haque Aome
相关产品推荐
相关产品推荐

