You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在非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))
        }
    }
}

注意事项

  1. Accompanist依赖:需要在build.gradle中添加权限库依赖(版本请匹配你的Compose版本):
    implementation "com.google.accompanist:accompanist-permissions:0.32.0"
    
  2. 重名处理:如果有多个同名联系人,上述代码只会返回第一个,可修改查询逻辑返回所有匹配结果。
  3. 号码格式化:查询到的号码可能包含空格、区号等,可根据需求用PhoneNumberUtils做格式化。

内容的提问来源于stack exchange,提问作者KawaiiGiu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 07:53:41