如何在非Activity环境下使用Kotlin Compose获取联系人号码
在非Activity环境下用Kotlin Compose获取联系人号码
核心思路
不需要依赖Activity实例,借助AndroidViewModel获取Application Context来操作ContentResolver,同时用Compose的权限处理库完成权限申请,最后通过Flow将查询结果通知界面。
步骤1:添加权限
在AndroidManifest.xml中添加读取联系人的权限:
<uses-permission android:name="android.permission.READ_CONTACTS" />
步骤2:实现ViewModel(非Activity依赖)
用AndroidViewModel获取Application Context,在IO线程执行联系人查询,避免阻塞主线程:
import android.app.Application import android.content.ContentResolver import android.database.Cursor import android.provider.ContactsContract import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch class ContactViewModel(application: Application) : AndroidViewModel(application) { private val _contactNumber = MutableStateFlow<String?>(null) val contactNumber: StateFlow<String?> = _contactNumber // 根据联系人姓名查询号码 fun getContactNumberByName(targetName: String) { viewModelScope.launch(Dispatchers.IO) { val contentResolver: ContentResolver = getApplication<Application>().contentResolver // 查询联系人手机号的URI val cursor: Cursor? = contentResolver.query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER), "${ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME} = ?", arrayOf(targetName), null ) cursor?.use { if (it.moveToFirst()) { val number = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)) _contactNumber.value = number } else { _contactNumber.value = null // 未找到匹配联系人 } } } } }
步骤3:Compose界面实现
用Accompanist权限库处理权限申请,监听ViewModel的Flow结果更新UI:
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberPermissionState @OptIn(ExperimentalPermissionsApi::class) @Composable fun ContactQueryScreen() { val viewModel: ContactViewModel = viewModel() val contactNumber = viewModel.contactNumber.collectAsState() val readContactsPermission = rememberPermissionState(android.Manifest.permission.READ_CONTACTS) Column(modifier = Modifier.padding(16.dp)) { Button( onClick = { if (readContactsPermission.hasPermission) { // 替换为你要查询的联系人姓名 viewModel.getContactNumberByName("张三") } else { readContactsPermission.launchPermissionRequest() } } ) { Text(text = "获取联系人号码") } contactNumber.value?.let { Text(text = "查询到的号码:$it", modifier = Modifier.padding(top = 16.dp)) } ?: run { Text(text = "未找到联系人或未授权", modifier = Modifier.padding(top = 16.dp)) } } }
注意事项
- Accompanist依赖:需要在
build.gradle中添加权限库依赖(版本请匹配你的Compose版本):implementation "com.google.accompanist:accompanist-permissions:0.32.0" - 重名处理:如果有多个同名联系人,上述代码只会返回第一个,可修改查询逻辑返回所有匹配结果。
- 号码格式化:查询到的号码可能包含空格、区号等,可根据需求用
PhoneNumberUtils做格式化。
内容的提问来源于stack exchange,提问作者KawaiiGiu
相关产品推荐
相关产品推荐

