在 Kotlin 的 suspendCoroutine 中,AndroidNetworking 从不 returns
AndroidNetworking never returns when in suspendCoroutine in Kotlin
我正在试验新的协同程序,并尝试将它们合并到一个现有的项目中,其中包含一些丑陋的 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 returns。
我的问题是,它从来没有 returns。即使我在 onResponse
、onError
等中调试,它也永远不会从后端返回。
但是如果我删除 = suspendCoroutine
它可以工作,至少它 returns,但是 await 不会工作,因为执行将继续。
如何解决这个问题,让 await()
实际上等待异步调用回调?
非常感谢!
尝试这样的事情:
fun someFunction() {
val jsonObject: JSONObject? = runBlocking {
callGetSomething()
}
// do something with jsonObject
}
suspend fun callGetSomething(): JSONObject? = suspendCoroutine {
Something().getSomethingFromBackend { response, error ->
it.resume(response)
}
}
您的主要问题是您正在尝试使用 runBlocking
,这会抵消您为项目引入异步网络库和协程所做的所有工作。如果你真的想在等待结果时阻塞,那么就使用阻塞网络调用。
如果您想继续使用异步网络操作,那么忘记您的阻塞 someFunction
并直接使用 launch
块中的 callGetSomething
:
override fun onSomeEvent(...) {
this.launch {
val result = callGetSomething(...)
// use the result, you're on the GUI thread here
}
}
以上假定您的 this
是 Activity
、Fragment
、ViewModel
或实现 CoroutineScope
的类似对象。查看其 documentation 了解如何实现。
好的,谢谢大家的帮助。
我将解释我是如何发现问题以及如何解决问题的。
在研究了更多有关回调和协程的信息后,我决定在回调期间记录线程名称,就像这样。 Log.d("TAG", "Execution thread: "+Thread.currentThread().name)
.
这让我意识到所有的回调(错误和成功),实际上 运行 在主线程上, NOT 在协程上为它设置。
所以我协程中的代码在 D/TAG: Execution thread: DefaultDispatcher-worker-1
中运行,但回调在 D/TAG: Execution thread: main
中运行。
解决方法
避免runBlocking
:这会阻塞主线程,在这种情况下,这是回调永远不会返回的原因。
在启动协程时使用Dispatchers.Main
,所以我实际上可以在UI.
中使用返回值
使用 suspendCoroutine
和 resume()
和 resumeWithException
(感谢@Sergey)
例子
fun someFunction() {
GlobalScope.launch(Dispatchers.Main) {
result = GlobalScope.async { callGetSomething() }.await()
...
}
}
suspend fun callGetSomething(): JSONObject? = suspendCoroutine {
Something().getSomethingFromBackend {something
...
it.resume(something)
}
}
我正在试验新的协同程序,并尝试将它们合并到一个现有的项目中,其中包含一些丑陋的 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 returns。
我的问题是,它从来没有 returns。即使我在 onResponse
、onError
等中调试,它也永远不会从后端返回。
但是如果我删除 = suspendCoroutine
它可以工作,至少它 returns,但是 await 不会工作,因为执行将继续。
如何解决这个问题,让 await()
实际上等待异步调用回调?
非常感谢!
尝试这样的事情:
fun someFunction() {
val jsonObject: JSONObject? = runBlocking {
callGetSomething()
}
// do something with jsonObject
}
suspend fun callGetSomething(): JSONObject? = suspendCoroutine {
Something().getSomethingFromBackend { response, error ->
it.resume(response)
}
}
您的主要问题是您正在尝试使用 runBlocking
,这会抵消您为项目引入异步网络库和协程所做的所有工作。如果你真的想在等待结果时阻塞,那么就使用阻塞网络调用。
如果您想继续使用异步网络操作,那么忘记您的阻塞 someFunction
并直接使用 launch
块中的 callGetSomething
:
override fun onSomeEvent(...) {
this.launch {
val result = callGetSomething(...)
// use the result, you're on the GUI thread here
}
}
以上假定您的 this
是 Activity
、Fragment
、ViewModel
或实现 CoroutineScope
的类似对象。查看其 documentation 了解如何实现。
好的,谢谢大家的帮助。 我将解释我是如何发现问题以及如何解决问题的。
在研究了更多有关回调和协程的信息后,我决定在回调期间记录线程名称,就像这样。 Log.d("TAG", "Execution thread: "+Thread.currentThread().name)
.
这让我意识到所有的回调(错误和成功),实际上 运行 在主线程上, NOT 在协程上为它设置。
所以我协程中的代码在 D/TAG: Execution thread: DefaultDispatcher-worker-1
中运行,但回调在 D/TAG: Execution thread: main
中运行。
解决方法
避免
runBlocking
:这会阻塞主线程,在这种情况下,这是回调永远不会返回的原因。在启动协程时使用
Dispatchers.Main
,所以我实际上可以在UI. 中使用返回值
使用
suspendCoroutine
和resume()
和resumeWithException
(感谢@Sergey)
例子
fun someFunction() {
GlobalScope.launch(Dispatchers.Main) {
result = GlobalScope.async { callGetSomething() }.await()
...
}
}
suspend fun callGetSomething(): JSONObject? = suspendCoroutine {
Something().getSomethingFromBackend {something
...
it.resume(something)
}
}