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

Jetpack Compose调用@Composable函数报错及SMS定位功能问题

问题根源与解决方案

核心问题

你把发送SMS的逻辑封装在了带@Composable注解的函数里,但Icon的clickable事件回调是普通Lambda函数,不属于Composable上下文,因此无法直接调用带@Composable的函数——这就是报错的原因。发送SMS属于业务逻辑/副作用,不该放在Composable(UI渲染函数)中。


具体解决步骤

1. 抽离发送SMS的普通函数

把发送SMS的逻辑写成普通函数,脱离Composable上下文:

fun sendLocationSMS(context: Context, phoneNumber: String, latitude: Double, longitude: Double) {
    // 构造预填的位置消息(可替换为地图链接格式)
    val smsContent = "我的当前位置:\n纬度:$latitude\n经度:$longitude\n地图链接:https://maps.google.com/?q=$latitude,$longitude"
    
    // 调用系统SMS应用的Intent
    val smsIntent = Intent(Intent.ACTION_SENDTO).apply {
        data = Uri.parse("smsto:$phoneNumber") // 指定收件人
        putExtra("sms_body", smsContent) // 预填消息内容
    }
    
    // 检查是否有能处理该Intent的应用
    if (smsIntent.resolveActivity(context.packageManager) != null) {
        context.startActivity(smsIntent)
    }
}

2. 在Click事件中执行完整逻辑

在Icon的clickable回调里,依次完成「权限检查→获取位置→从Firestore拿手机号→调用发送函数」的流程:

@Composable
fun LocationSendIcon() {
    val context = LocalContext.current
    // 权限申请Launcher
    val smsPermissionLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            // 权限通过后执行后续逻辑
            handleLocationSend()
        } else {
            Toast.makeText(context, "需要发送短信权限才能完成操作", Toast.LENGTH_SHORT).show()
        }
    }

    val locationPermissionLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            handleLocationSend()
        } else {
            Toast.makeText(context, "需要位置权限才能获取当前位置", Toast.LENGTH_SHORT).show()
        }
    }

    Icon(
        imageVector = Icons.Default.LocationOn,
        contentDescription = "发送当前位置",
        modifier = Modifier.clickable {
            // 先检查权限
            when {
                ContextCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED -> {
                    smsPermissionLauncher.launch(Manifest.permission.SEND_SMS)
                }
                ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED -> {
                    locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
                }
                else -> {
                    handleLocationSend()
                }
            }
        }
    )

    // 封装核心逻辑的普通函数
    fun handleLocationSend() {
        // 1. 获取当前设备位置(替换为你的实际位置获取逻辑,比如用LocationManager或Google Play Services)
        val currentLocation = getCurrentDeviceLocation(context) ?: run {
            Toast.makeText(context, "无法获取当前位置", Toast.LENGTH_SHORT).show()
            return
        }

        // 2. 从Firestore异步获取收件人手机号
        Firebase.firestore.collection("recipients").document("default_recipient")
            .get()
            .addOnSuccessListener { doc ->
                val phoneNumber = doc.getString("phone_number") ?: run {
                    Toast.makeText(context, "未找到收件人手机号", Toast.LENGTH_SHORT).show()
                    return@addOnSuccessListener
                }
                // 3. 调用发送SMS的普通函数
                sendLocationSMS(
                    context = context,
                    phoneNumber = phoneNumber,
                    latitude = currentLocation.latitude,
                    longitude = currentLocation.longitude
                )
            }
            .addOnFailureListener { e ->
                Toast.makeText(context, "获取收件人信息失败:${e.message}", Toast.LENGTH_SHORT).show()
            }
    }
}

3. 配置权限

在AndroidManifest.xml中添加必要权限:

<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Android 13+ 如需后台定位需额外添加 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

关键注意点

  • Composable函数仅用于UI渲染,所有业务逻辑、异步操作、系统调用都应该放在普通函数或ViewModel中。
  • Firestore查询是异步操作,必须在回调中处理结果,不能直接同步获取。
  • 权限申请是Android的强制要求,必须在执行敏感操作前完成检查与申请。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:52:13