试图深入了解协程。我有一个suspendCancellableCoroutine
,应该可以获取网络响应。我在Charles中看到网络呼叫已调度并成功返回。但是,我的应用程序只是挂在网络请求行上。
private suspend fun fetchVisualElementsFromServer(clubId: String): VisualElements {
return suspendCancellableCoroutine { cont ->
visualElementsService.fetchVisualElementsForClub(clubId)
.enqueue(object : Callback<ResultVisualElements> {
override fun onResponse(
call: Call<ResultVisualElements>,
response: Response<ResultVisualElements>
) {
if (response.isSuccessful) {
response.body()?.let {
if (it.result == RESULT_SUCCESS) {
saveVisualElementsResponseInSharedPreferences(it.visual_elements)
cont.resume (it.visual_elements)
} else {
cont.cancel() //edit
}
} ?: cont.cancel() //edit
} else {
cont.cancel(IOException("${response.code()}: ${response.errorBody()}"))
}
}
override fun onFailure(call: Call<ResultVisualElements>, t: Throwable) {
Timber.e(t, "visual elements fetch failed")
cont.cancel() // edit
}
})
}
}
它挂在这里:
VisualElementsService.kt
fun fetchVisualElementsForClub(clubId: String): Call<ResultVisualElements> {
return dataFetcherService.getVisualElementsForClub(clubId)
}
我在这里想念什么?我试图使fetchVisualElementsForClub()
成为暂停函数,但这只会使suspendCancellableCoroutine
抛出Suspension functions can only be called within coroutine body
错误。但是我认为他在一个协程体内吗?
任何帮助表示赞赏。谢谢。
编辑
我在下面回应Rene的回答,我想补充一些内容。
是的,我错过了三个cont.cancel()调用。我修改了OP。好点。
我在suspendCancellableCoroutine
上都有断点,因此会遇到任何可能的情况(成功,失败等)。但是该回调从不注册。
想知道在fetchVisualElementsForClub()
中是否缺少将回调传递到suspendCancellableCoroutine
的内容。那似乎就是挂了的地方。
答案 0 :(得分:2)
您必须在回调处理的每个分支上调用cont.resume()
或cont.cancel()
。
但是在您的示例中,至少缺少了三种情况。
it.result
不是RESULT_SUCCESS
,则您什么也没叫。onFailure
中出了点问题,您什么也没叫。只要未调用resume
或cancel
,协程将保持挂起状态,即挂起。
答案 1 :(得分:-1)
当您使用suspend关键字时,您正在告诉应在协程内部调用该函数,例如:
suspend fun abc(){
return
}
当您要调用上面的函数时,必须在诸如以下的协程中调用它:
GlobalScope.launch {
abc()
}