【问题标题】:AndroidNetworking never returns when in suspendCoroutine in Kotlin在 Kotlin 的 suspendCoroutine 中时,AndroidNetworking 永远不会返回
【发布时间】:2019-04-18 23:54:45
【问题描述】:

我正在试验新的协程,并尝试将它们合并到现有项目中,并带有一些丑陋的 AndroidNetworking api 请求。

所以我当前的 api 请求正在使用这样的回调......

fun getSomethingFromBackend(callback: (response: JSONArray?, error: String?) -> Unit) {
  AndroidNetworking.get(...).build().getAsJSONObject(object : JSONObjectRequestListener {
    override fun onResponse(response: JSONObject) { ... }
    override fun onError(error: ANError) { ... }
  }
}

现在我正在尝试合并这样的协程......

fun someFunction() = runBlocking {
  async { callGetSomething() }.await()
}

suspend fun callGetSomething(): JSONObject? = suspendCoroutine {
  Something().getSomethingFromBackend {something
    ...
    it.resume(something)
  }
}

如您所见,我正在尝试在协程中运行 callGetSomething,然后暂停协程直到异步 API 返回。

我的问题是,它永远不会返回。即使我在onResponseonError 等中调试,它也永远不会从后端返回。

但是如果我删除= suspendCoroutine 它可以工作,至少它会返回,但是等待不会正常工作,因为执行会继续。

我该如何解决这个问题,以便 await() 实际上等待异步调用回调?

非常感谢!

【问题讨论】:

  • 你确定getSomethingFromBackend回调总是被调用吗?
  • 另外,如果someFunction 和回调应该从同一个线程调用,那么它不会发生,因为你用runBlocking 阻止了它

标签: android kotlin coroutine kotlinx.coroutines


【解决方案1】:

好的,谢谢大家的帮助。 我将解释我是如何发现问题的以及我是如何解决它的。

在研究了有关回调和协程的更多信息后,我决定在回调期间记录线程名称,就像这样。 Log.d("TAG", "Execution thread: "+Thread.currentThread().name).

这让我意识到所有回调(错误和成功)实际上在主线程上运行,而 NOT 在我为其设置的协程上运行。

所以我的协程中的代码在D/TAG: Execution thread: DefaultDispatcher-worker-1 上运行,但回调在D/TAG: Execution thread: main 上运行。

解决方案

  1. 避免runBlocking:这将阻塞主线程,在这种情况下,这就是回调永远不会返回的原因。

  2. 在启动协程时使用Dispatchers.Main,这样我就可以在UI中实际使用返回的值了。

  3. 使用suspendCoroutineresume()resumeWithException(感谢@Sergey)

示例

fun someFunction() {
  GlobalScope.launch(Dispatchers.Main) {
    result = GlobalScope.async { callGetSomething() }.await()
    ...
  }
}

suspend fun callGetSomething(): JSONObject? = suspendCoroutine {
  Something().getSomethingFromBackend {something
    ...
    it.resume(something)
  }
}

【讨论】:

    【解决方案2】:

    您的主要问题是您正在尝试使用runBlocking,它取消了您为将异步网络库和协程引入项目所做的所有工作。如果你真的想在等待结果时阻塞,那么只需使用阻塞网络调用。

    如果您想继续使用异步网络操作,那么请忘记您的阻止 someFunction 并直接使用 launch 块中的 callGetSomething

    override fun onSomeEvent(...) {
       this.launch {
         val result = callGetSomething(...)
         // use the result, you're on the GUI thread here
       }
    }
    

    以上假设您的thisActivityFragmentViewModel 或实现CoroutineScope 的类似对象。请参阅其documentation 了解如何实现这一目标。

    【讨论】:

      【解决方案3】:

      试试这样的:

      fun someFunction() {
          val jsonObject: JSONObject? = runBlocking {
              callGetSomething()
          }
          // do something with jsonObject
      }
      
      suspend fun callGetSomething(): JSONObject? = suspendCoroutine {
          Something().getSomethingFromBackend { response, error ->
              it.resume(response)
          }
      }
      

      【讨论】:

      • 感谢@Sergey,但它不起作用,AndroidNetworking.get 正在被调用,协程正在等待,但回调永远不会被调用。没有响应,没有错误,什么都没有……
      猜你喜欢
      • 2019-11-15
      • 2011-07-25
      • 1970-01-01
      • 2012-10-02
      • 2013-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多