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

Kotlin协程获取Location报IllegalStateException: Already resumed问题求助

解决Kotlin协程获取位置更新时的Already resumed异常

这个问题的根源很明确:你在await函数里注册的LocationListener没有在协程续体(Continuation)完成后被移除,再加上每次触发onTextChanged都会发起新的协程调用await,导致注册了多个重复的Listener。当第二次位置更新到来时,之前的Listener还在监听列表中,会尝试再次调用cont.resume(),但此时对应的协程续体已经完成,直接触发Already resumed异常。

问题拆解

你的await逻辑存在两个核心问题:

  • 调用requestLocationUpdates后,Listener会一直绑定在LocationManager上,除非主动移除。第一次位置更新触发cont.resume()后,Listener并没有被清理,后续的位置更新会持续触发它。
  • 每次输入满足条件就启动新协程调用await,会不断新增Listener,这些Listener都会在位置变化时尝试resume各自的续体,很容易出现重复resume的情况。

解决方案

我们需要确保Listener在完成任务后被及时移除,同时优化请求逻辑避免重复注册。

1. 修改await函数,确保Listener被清理

把Listener提取为变量,在收到位置或异常时主动移除它,避免后续触发无效的resume操作:

private suspend fun LocationManager.await(locationProvider: String): Location? = suspendCoroutine { cont ->
    val listener = object : LocationListener {
        override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
        override fun onProviderEnabled(provider: String?) {}
        override fun onProviderDisabled(provider: String?) {
            removeUpdates(this) // 移除Listener,避免后续触发
            cont.resumeWithException(UnknownLocationException())
        }
        override fun onLocationChanged(location: Location?) {
            removeUpdates(this) // 关键:收到位置后立即解绑Listener
            cont.resume(location)
        }
    }
    try {
        requestLocationUpdates(locationProvider, 0, 0f, listener)
    } catch (ex: SecurityException) {
        cont.resumeWithException(ex)
    }
}

2. 优化TextWatcher逻辑,避免重复发起请求

添加一个标志位控制是否正在等待位置更新,防止短时间内重复触发协程请求:

// 在Activity类中添加标志位
private var isWaitingForLocation = false

override fun onConnected(p0: Bundle?) {
    val locationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    input_address.addTextChangedListener(object: TextWatcher{
        override fun afterTextChanged(p0: Editable?) {}
        override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
        override fun onTextChanged(query: CharSequence?, p1: Int, p2: Int, p3: Int) {
            if (query.toString().length >= 4 && !isWaitingForLocation) {
                isWaitingForLocation = true
                launch(UI) {
                    try {
                        val locationUpdated = locationManager.await(LocationManager.GPS_PROVIDER)
                        input_name.text = Editable.Factory.getInstance().newEditable(locationUpdated.toString())
                    } catch (e: Exception) {
                        // 这里可以添加异常处理,比如提示用户定位失败
                    } finally {
                        isWaitingForLocation = false // 请求完成后重置标志位
                    }
                }
            }
        }
    })
}

额外提示

如果你的需求只是获取单次位置更新,也可以直接使用LocationManager.requestSingleUpdate方法,它会在获取到一次位置后自动停止监听,能更简洁地避免上述问题(注意API级别和权限适配即可)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:59:26