Android 联系人选择器是一个标准化的可浏览界面,供用户与您的应用分享联系人。该选择器可在搭载 Android 17 或更高版本的设备上使用,可提供一种注重隐私保护的替代方案,以取代范围广泛的 READ_CONTACTS 权限。您的应用不会请求访问用户的整个地址簿,而是指定所需的数据字段(例如电话号码或电子邮件地址),然后用户选择要分享的特定联系人。这只会授予您的应用对所选数据的读取权限,从而确保精细控制,同时提供一致的用户体验,并具有内置的搜索、个人资料切换和多选功能,而无需构建或维护界面。
集成联系人选择器
如需集成联系人选择器,请使用 Intent.ACTION_PICK_CONTACTS intent。此 intent 会启动选择器,并将所选联系人返回给您的应用。
与旧版 ACTION_PICK 不同,借助联系人选择器,您可以同时指定应用所需的多个数据字段。您可以使用 Intent.EXTRA_REQUESTED_DATA_FIELDS 来实现此目的,并传递 ContactsContract.CommonDataKinds 中定义的 MIME 类型的 ArrayList<String>。
常见的 MIME 类型包括:
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE
启动选择器
使用 registerForActivityResult 和 StartActivityForResult 合约启动选择器。您可以配置 intent 以允许单选或多选。
选择单个联系人
在此示例中,应用仅请求电话号码。选择器将过滤列表,仅显示包含电话号码的联系人,并允许用户选择特定号码。
Kotlin
// Define the specific data fields you need
val requestedFields = arrayListOf(
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
// Set up the intent
val pickContactIntent = Intent(Intent.ACTION_PICK_CONTACTS).apply {
type = ContactsContract.Contacts.CONTENT_TYPE
putStringArrayListExtra(Intent.EXTRA_REQUESTED_DATA_FIELDS, requestedFields)
}
// Launch the picker
pickContactLauncher.launch(pickContactIntent)
Java
// Define the specific data fields you need
ArrayList<String> requestedFields = new ArrayList<>();
requestedFields.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
// Set up the intent
Intent pickContactIntent = new Intent(Intent.ACTION_PICK_CONTACTS);
pickContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE);
pickContactIntent.putStringArrayListExtra(Intent.EXTRA_REQUESTED_DATA_FIELDS,
requestedFields);
// Launch the picker
pickContactLauncher.launch(pickContactIntent);
选择多个联系人
如需启用多选,请添加 Intent.EXTRA_ALLOW_MULTIPLE extra。您可以选择性地限制用户可选择的商品数量。
Kotlin
val requestedFields = arrayListOf(
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
)
val pickMultipleIntent = Intent(Intent.ACTION_PICK_CONTACTS).apply {
type = ContactsContract.Contacts.CONTENT_TYPE
putStringArrayListExtra(Intent.EXTRA_REQUESTED_DATA_FIELDS, requestedFields)
// Enable multi-select
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
// Optional: Set a custom limit (max 50 recommended)
putExtra(Intent.EXTRA_SELECTION_LIMIT, 10)
}
pickMultipleLauncher.launch(pickMultipleIntent)
处理结果
当用户完成选择后,系统会返回 RESULT_OK 和会话 URI。此 URI 授予对所选数据的临时读取权限。
您可以使用标准 ContentResolver 查询此 URI。生成的 Cursor 包含所请求的数据字段,并遵循 ContactsContract.Data 的架构。
Kotlin
private val pickContactLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// The result data contains the Session URI
val sessionUri = result.data?.data
sessionUri?.let { uri ->
processSelectedContacts(uri)
}
} else {
// User cancelled the picker
}
}
private fun processSelectedContacts(sessionUri: Uri) {
// Define the projection (columns) you want to retrieve
val projection = arrayOf(
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
ContactsContract.Data.MIMETYPE,
ContactsContract.Data.DATA1 // Generic data column (Phone number, Email, etc.)
)
contentResolver.query(sessionUri, projection, null, null, null)?.use { cursor ->
val mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)
val dataIdx = cursor.getColumnIndex(ContactsContract.Data.DATA1)
val nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)
while (cursor.moveToNext()) {
val mimeType = cursor.getString(mimeTypeIdx)
val dataValue = cursor.getString(dataIdx)
val name = cursor.getString(nameIdx)
when (mimeType) {
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
Log.d("ContactPicker", "Picked Phone: $dataValue for $name")
}
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> {
Log.d("ContactPicker", "Picked Email: $dataValue for $name")
}
}
}
}
}
向后兼容性
对于以 Android 17 及更高版本为目标平台的应用,系统会自动升级现有的 Intent.ACTION_PICK intent 以使用新的联系人选择器界面。
如果您的应用已使用 ACTION_PICK,则无需更改代码即可获得新界面。不过,如需使用新功能(例如接收单个 Uri 来查询联系人数据、在个人资料和工作资料之间切换或发出多个数据字段请求),您必须更新实现以使用 Intent.ACTION_PICK_CONTACTS 或新的 intent extra。
在旧版 SDK 上进行测试
您可以在搭载 Android 17 及更高版本的设备上测试新的选择器行为,即使您的应用以较低的 SDK 版本为目标平台,也可以通过向 ACTION_PICK intent 添加 EXTRA_USE_SYSTEM_CONTACTS_PICKER 布尔值 extra 来实现此目的。
最佳做法
- 仅请求所需权限:如果您的应用只需要发送短信,请请求
Phone.CONTENT_ITEM_TYPE。选择器会自动过滤掉没有电话号码的联系人,从而为用户提供更简洁的界面。 - 立即持久保留数据:会话 URI 授予临时读取权限。如果您需要在应用进程被终止后访问此联系信息,则应用必须保留联系人数据。
- 请勿依赖账号数据:为保护用户隐私并防止指纹识别,系统会从结果中剥离账号特定的元数据。