【问题标题】:kotlin coroutine throws java.lang.IllegalStateException: Already resumed, but got value Locationkotlin 协程抛出 java.lang.IllegalStateException: 已经恢复,但得到值 Location
【发布时间】:2018-01-12 13:32:44
【问题描述】:

总的来说,我对 Kotlin 协程和 Android 开发还很陌生。 在尝试了解它的工作原理时,我遇到了一个似乎无法解决的错误。

从一项基本活动中,我尝试连接到 googleApiClient。权限没问题。 我希望使用 kotlin 协程以直接方式从 LocationManager 获取位置更新,以便稍后使用此 Location 对象。 我第一次在模拟器中更改位置时效果很好,第二次更改位置时,它崩溃了 除了这样的例外:

FATAL EXCEPTION: main
    Process: com.link_value.eventlv, PID: 32404
    java.lang.IllegalStateException: Already resumed, but got value Location[gps 48.783000,2.516180 acc=20 et=+59m16s372ms alt=0.0 {Bundle[mParcelledData.dataSize=40]}]
    at kotlinx.coroutines.experimental.AbstractContinuation.resumeImpl(AbstractContinuation.kt:79)
    at kotlinx.coroutines.experimental.AbstractContinuation.resume(AbstractContinuation.kt:72)
    at com.link_value.eventlv.View.Create.NewEventLvActivity$await$2$1.onLocationChanged(NewEventLvActivity.kt:100)
    at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:297)
    at android.location.LocationManager$ListenerTransport.-wrap0(LocationManager.java)
    at android.location.LocationManager$ListenerTransport$1.handleMessage(LocationManager.java:242)
     at android.os.Handler.dispatchMessage(Handler.java:102)
     at android.os.Looper.loop(Looper.java:154)
     at android.app.ActivityThread.main(ActivityThread.java:6077)
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)



override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_new_event_lv)

    askForUserLocation()
    val locationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    val presenter = CreateEventPresenterImpl(this@NewEventLvActivity)

    googleApiClient = GoogleApiClient.Builder(this@NewEventLvActivity)
            .enableAutoManage(this /* FragmentActivity */,
                    this /* OnConnectionFailedListener */)
            .addApi(Places.GEO_DATA_API)
            .addConnectionCallbacks(this)
            .build()
}
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) {
                launch(UI) {
                    val locationUpdated = locationManager.await(LocationManager.GPS_PROVIDER)
                    input_name.text = Editable.Factory.getInstance().newEditable(locationUpdated.toString())
                }
            }
        }
    })
}

private suspend fun LocationManager.await(locationProvider: String): Location? = suspendCoroutine { cont ->
    try {
        requestLocationUpdates(locationProvider, 0, 0.toFloat(), object : LocationListener {
            override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
            }

            override fun onProviderEnabled(p0: String?) {
            }

            override fun onProviderDisabled(p0: String?) {
                cont.resumeWithException(UnknownLocationException())
            }

            override fun onLocationChanged(location: Location?) {
                cont.resume(location)
            }
        })
    } catch (ex: SecurityException) {
        cont.resumeWithException(ex)
    }
}

就好像 Kotlin 使用相同的延续。我不知道我做错了什么以及为什么它第二次崩溃。有人可以启发我。 提前考虑。

【问题讨论】:

  • 您注册了LocationListener,已多次恢复。我认为,出于您的目的,更倾向于使用 Channels 通信,您将在其中放置新的位置值,并从中读取值并在 UI 中的文本后更新。

标签: android kotlin locationmanager kotlinx.coroutines


【解决方案1】:

根据文档,只能单次恢复 Continuation。第二份简历将抛出 IllegalStateException 并带有“已恢复,但收到 $proposedUpdate”消息。您可以添加额外的检查 continuation.isActive 以防止出现此异常。最好将 Channels 用于位置更新等多个回调。

【讨论】:

  • 顺便说一句,isActive 仅在 CancellableContinuation 上。您可以使用suspendCancellableCoroutine 函数获得。
  • 或者你可以使用callbackFlow进行多个回调
【解决方案2】:

我最近在我们的项目中使用的第 3 方 API 之一遇到了这个问题。问题仅出在 API 方面,因为回调在底层实现中被多次调用,unsubscribe 方法并没有立即从 API 中删除回调。

所以,我选择的方法只是添加一个布尔标志。虽然,它可以通过 Kotlin 的 Flow.mapLatest/collectLatest 左右以更优雅的方式解决。

suspend fun singularContentUpdate(): Data =
    suspendCancellableCoroutine { continuation ->
        var resumed = false

        val callback = object : Callback {
            override fun onDataReady(d: Data) {
                api.unsubscribe(this)

                if (!resumed) {
                    continuation.resume(d)
                    resumed = true
                }
            }
        }

        continuation.invokeOnCancellation {
            api.unsubscribe(callback)
        }

        api.subscribe(subscriber)

        api.refresh()
    }

【讨论】:

  • 您不需要自己维护恢复状态,suspendCancellableCoroutine 会为您完成。使用continuation.isActive检查协程是否恢复
  • @bensadiku 我已经尝试过了,不幸的是它变得不活跃对我来说太晚了。并且api 第二次回调调用发生得太早了。所以,除了使用这个丑陋的标志外,我别无选择:(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 2021-12-06
  • 2013-03-01
  • 2021-09-04
相关资源
最近更新 更多