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

Android Kotlin中fusedLocationProviderClient.getCurrentLocation异常问题

问题

开发一款Android(Kotlin)应用,需求为按需获取位置(仅点击悬浮按钮时获取经纬度并显示在TextView中),无需持续更新位置(不使用requestLocationUpdates)。权限检查逻辑已实现且无问题。

在Motorola One Vision(Android 11)设备上步行测试时,出现异常行为:

  • 首次点击定位按钮数秒后获取到坐标;
  • 步行5-10米(约8-15秒)后再次点击按钮,会出现三种情况:
    1. 3-5秒后获取到新坐标,状态栏显示谷歌定位图标,此为正常情况;
    2. 立即获取到步行前的旧坐标,状态栏无谷歌定位图标,瞬间弹出“Location obtained”提示Toast,需等待数秒后多次点击才能获取新坐标;
    3. 瞬间获取到新坐标,弹出“Location obtained”提示Toast,但状态栏无谷歌定位图标。

疑问:

  1. 第二种情况产生的原因是什么?如何解决,实现每次点击都获取新坐标(即使需等待数秒)?
  2. 第三种情况的原因是什么?

触发定位的代码如下:

@SuppressLint("MissingPermission")
fun getLocation(){
    if (isGPSActivated){
        fusedLocationProviderClient.getCurrentLocation(
            LocationRequest.PRIORITY_HIGH_ACCURACY,
            object : CancellationToken() {
                override fun onCanceledRequested(p0: OnTokenCanceledListener) = CancellationTokenSource().token
                override fun isCancellationRequested() = false
            })
            .addOnSuccessListener { location: Location? ->
                if (location == null) {
                    Toast.makeText(
                        this,
                        "Can't get location, try again",
                        Toast.LENGTH_SHORT
                    ).show()
                }

                else {
                    updateLocationInViewModel(location)
                    
                    Toast.makeText(
                        this,
                        "Location obtained",
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
            .addOnFailureListener {
                Toast.makeText(
                    this,
                    "Can't get location, try again",
                    Toast.LENGTH_SHORT
                ).show()
            }
    }
}

fun updateLocationInViewModel(currentLocation: Location){
    individuoViewModel._localization.value = currentLocation
}

已为individuoViewModel._localization设置观察者,检测到新值时会刷新TextView。


解答

第二种情况(返回旧坐标)的原因与解决方法

原因

getCurrentLocation()的核心逻辑是优先返回系统缓存的最近位置,只要这个缓存位置的时间戳在系统允许的“新鲜度”范围内(高精准度优先级下通常是几秒到几十秒),就不会触发新的定位请求,直接返回缓存值。

你步行后短时间内点击按钮时,缓存的旧位置还未被系统判定为过期,因此直接返回旧坐标,不会唤起定位硬件(状态栏无图标),也不会等待新的定位结果。

解决方法

要强制获取最新位置,需绕过getCurrentLocation()的缓存逻辑,推荐使用单次定位回调的方式:

@SuppressLint("MissingPermission")
fun getLocation() {
    if (isGPSActivated) {
        val locationRequest = LocationRequest.create().apply {
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
            numUpdates = 1 // 仅请求一次定位更新
            fastestInterval = 0
            interval = 0
        }

        val locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                super.onLocationResult(locationResult)
                locationResult.lastLocation?.let { location ->
                    updateLocationInViewModel(location)
                    Toast.makeText(this@YourActivity, "Location obtained", Toast.LENGTH_SHORT).show()
                } ?: run {
                    Toast.makeText(this@YourActivity, "Can't get location, try again", Toast.LENGTH_SHORT).show()
                }
                // 立即移除回调,避免持续监听
                fusedLocationProviderClient.removeLocationUpdates(this)
            }
        }

        fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
            .addOnFailureListener {
                Toast.makeText(this@YourActivity, "Can't get location, try again", Toast.LENGTH_SHORT).show()
                fusedLocationProviderClient.removeLocationUpdates(locationCallback)
            }
    }
}

如果坚持使用getCurrentLocation(),可以在返回结果时校验位置新鲜度,拒绝过期的旧坐标并重新请求:

// 在addOnSuccessListener的else分支中添加判断
else {
    val locationAge = System.currentTimeMillis() - location.time
    if (locationAge > 5000) { // 超过5秒的位置视为无效
        getLocation() // 重新发起请求
        return@addOnSuccessListener
    }
    updateLocationInViewModel(location)
    Toast.makeText(this, "Location obtained", Toast.LENGTH_SHORT).show()
}

第三种情况(返回新坐标但无状态栏图标)的原因

这种情况属于系统优化行为:此时系统缓存中已经存在足够新鲜的最新位置——可能是其他应用刚发起过定位请求,或者系统后台定位服务刚更新了位置,你的应用直接获取到缓存的新坐标,无需自己触发定位硬件,因此状态栏不会显示定位图标。这是正常现象,无需额外处理。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:40:27