从Android 11开始,出于隐私考虑,Google已经限制了第三方应用程序访问电话号码 在 Android 10 中,可以通过查询ContactsContract.CommonDataKinds.Phone.NUMBER来获取电话号码。但是,从 Android 11 开始,该列始终返回空值。

  1. 先在 AndroidManifest.xml 文件中添加 READ_CONTACTS 权限。
<uses-permission android:name="android.permission.READ_CONTACTS" />
  1. 下面请求使用 ActivityResultLauncher API仅适用于 androidx.activityandroidx.fragment 包,如果项目中没有引入这两个库,先在 build.gradle 文件中添加以下依赖项:
dependencies {
    // ...
    implementation "androidx.activity:activity-ktx:1.3.1"
    implementation "androidx.fragment:fragment-ktx:1.3.6"
}
  1. 完整示例代码
class MyFragment : Fragment() {

    private val contactsPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
        if (isGranted) {
            selectContactLauncher.launch(null)
        }
    }

    private val selectContactLauncher = registerForActivityResult(ActivityResultContracts.PickContact()) { uri ->
        uri?.let {
            val contactName = getContactName(it)
            val phoneNumber = getContactPhoneNumber(it)
            if (contactName != null && phoneNumber != null) {
                Toast.makeText(requireContext(), "Contact selected: $contactName, phone number: $phoneNumber", Toast.LENGTH_SHORT).show()
            } else {
                Toast.makeText(requireContext(), "Failed to retrieve contact information", Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun getContactName(contactUri: Uri): String? {
        val cursor = requireActivity().contentResolver.query(contactUri, null, null, null, null)
        cursor?.use {
            if (it.moveToFirst()) {
                val nameIndex = it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
                return it.getString(nameIndex)
            }
        }
        return null
    }

private fun getContactPhoneNumber(contactUri: Uri?): String? {
    if (contactUri == null) {
        return null
    }
    val projection = arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER)
    val selection = "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?"
    val selectionArgs = arrayOf(ContentUris.parseId(contactUri).toString())
    val cursor = requireActivity().contentResolver.query(
        ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
        projection,
        selection,
        selectionArgs,
        null
    )
    cursor?.use {
        if (it.moveToFirst()) {
            val phoneNumberIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
            return it.getString(phoneNumberIndex)
        }
    }
    return null
}

    private fun requestContactsPermission() {
        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
            selectContactLauncher.launch(null)
        } else {
            contactsPermissionLauncher.launch(Manifest.permission.READ_CONTACTS)
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // ...
        // 当用户单击按钮时请求读取联系人的权限并选择一个联系人
        myButton.setOnClickListener {
            requestContactsPermission()
        }
        // ...
    }
}

多嘴一句 由于use函数实现“try-with-resources”语句,use函数会在代码块完成时自动关闭资源,所以不用手动释放cursor 资源了 :)


Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐