【问题标题】:suspending a kotlin Coroutine does not work with volley暂停 kotlin 协程不适用于 volley
【发布时间】:2018-02-21 22:52:57
【问题描述】:

我编写了以下(测试)函数,通过 volley 和协程与 google maps api 对话。遗憾的是,当使用 suspendCoroutine 调用它时,它永远不会完成。如果我使用相同的函数,删除协程内容并实现“正常”回调,一切正常。我有点不知所措,这里有什么问题。有人可以帮忙吗?

代码一直执行到 Log.d(LOGTAG, "AFTERAFTER"),但永远不会到达 Log.d("findNaturalLocations", "Response: " + response)

suspend fun testNaturalLocations(tag: Tag, lastKnownUserLocation: 
Location): ArrayList<CLDistanceRequest> = suspendCoroutine { 
continuation ->

    Log.d("findNaturalLocations", "getDistanceAndTimeBetweenLocations")

    var distanceRequests = ArrayList<CLDistanceRequest>()

    val mapsBaseUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/"
    val mapsOutputFormat = "json"

    val location = "location=" + lastKnownUserLocation.latitude.toString() + "," + lastKnownUserLocation.longitude.toString()
    val radius = "radius=5000"
    val keyword = "keyword=Supermarket"
    val name = "name=Supermarket"
    val sensor = "sensor=true"
    val apiKey = "key=API_KEY"

    val finishedUrl = mapsBaseUrl + mapsOutputFormat + "?" + location + "&" + radius + "&" + keyword + "&" + name + "&" + sensor + "&" + apiKey

    Log.d(LOGTAG, finishedUrl)

    val jsObjectRequest = JsonObjectRequest(Request.Method.GET, finishedUrl, null,
            Response.Listener<JSONObject> { response ->
                Log.d("findNaturalLocations", "Response: " + response)

                var results = response.getJSONArray("results")

                // parse distanceRequests, ommitted for brevity

                continuation.resume(distanceRequests)
            },
            Response.ErrorListener { error ->
                Log.e("Error", error.localizedMessage, error)

                continuation.resumeWithException(error)
            }
    )

    Log.d(LOGTAG, "AFTER");

    jsObjectRequest.setShouldCache(false)
    CLGlobal.getRequestQueue().add(jsObjectRequest)

    Log.d(LOGTAG, "AFTERAFTER");
}

用一个简单的回调做同样的事情完美无缺。

var i = 0;
    runBlocking {
        val query = async(CommonPool) {
            i = this@CLTaskList.test2()
        }

        query.await()
    }

suspend fun test2():Int = suspendCoroutine<Int> { continuation ->
    Log.d("TESTTEST", "TEST2 CALLED")
    test {
        Log.d("TESTTEST", "CONTINUATION")
        continuation.resume(it)
    }
}

fun test(completionHandler: (Int) -> Unit) {
    Log.d("TESTTEST", "TEST CALLED")
    completionHandler(1)
}

【问题讨论】:

  • 如果它甚至没有到达Log.d("findNaturalLocations", "Response: " + response),那么它根本与协程无关,因为无论是否正确恢复协程,它都会到达该行。您的回调只是由于其他原因而无法执行。
  • 感谢您提供帮助,但事实并非如此。我没有测试它并且它有效,请参阅我的原始帖子。我的假设是网络请求是在后台线程上完成的,结果应该传递给启动请求的线程,并且协程可能正在运行。这个线程仍然被等待的协程阻塞,因此永远不会得到结果,基本上是死锁。
  • 您没有显示有效的基于回调的代码。协程挂起后没有在任何线程上运行(这是它们存在的原因)。如果执行到达AFTERAFTER 语句,这就是testNaturalLocations 调用以COROUTINE_SUSPENDED 返回值结束的地方。如果它没有到达回调中的日志语句,则意味着回调尚未运行。也许您的更广泛的代码做错了什么并阻塞了 UI 线程,但鉴于您在此处显示的代码,这是不可能的。
  • 啊,我明白了,您将第二个块称为“简单回调”,但这只是一个没有凌空抽射的琐碎代码。它没有展示任何相关内容,因为您甚至从未暂停协程 --- 当您从 suspendCoroutine 块内调用 resume 时,它只是立即返回结果,不涉及暂停。将 lambda 传递给 test 的事实并不重要,就像它被内联一样。为了演示任何相关的内容,您必须涉及另一个线程来执行您的回调并恢复继续。
  • 等等.. 您是否也在您的 GUI 代码中使用了runBlocking,而不仅仅是简单的示例?因为那会立即解释它。

标签: android kotlin android-volley kotlin-coroutines


【解决方案1】:

正如您在评论中所揭示的,这就是您运行查询的方式:

val query = async(CommonPool) { 
    this@CLTaskList.testNaturalLocations(tags[0], 
         CLGlobal.getInstance().mLastKnownLocation!!) 
} 
runBlocking<Unit> { query.await() }

你已经接近正确了,但是这段代码都是倒退的:

  1. 您让可挂起函数在线程池中运行,从而减轻一些工作线程的阻塞责任,直到您得到答案(它不会在意是否被阻塞);
  2. 您使用 runBlocking 阻止了您的 GUI 线程。

在正确的解决方案中,您不需要asyncCommonPoolrunBlocking,您只需要这样:

launch(UI) {
    val result = testNaturalLocations(tags[0], 
       CLGlobal.getInstance().mLastKnownLocation!!)
    // deal with the result right here
}

由于testNaturalLocations 是一个可挂起的函数,它不会阻塞您的UI 线程,当您编写的回调恢复它时,您的代码会继续执行下一行,并分配result

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-24
    • 2018-08-17
    • 2021-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多