暂停协程挂起

时间:2019-10-13 03:19:24

标签: android kotlin kotlin-coroutines

试图深入了解协程。我有一个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的内容。那似乎就是挂了的地方。

2 个答案:

答案 0 :(得分:2)

您必须在回调处理的每个分支上调用cont.resume()cont.cancel()。 但是在您的示例中,至少缺少了三种情况。

  1. 如果响应成功但没有提供任何正文,则您什么也不会打电话。
  2. 如果响应成功,则正文不为null,但是it.result不是RESULT_SUCCESS,则您什么也没叫。
  3. 如果onFailure中出了点问题,您什么也没叫。

只要未调用resumecancel,协程将保持挂起状态,即挂起。

答案 1 :(得分:-1)

当您使用suspend关键字时,您正在告诉应在协程内部调用该函数,例如:

suspend fun abc(){
   return
}

当您要调用上面的函数时,必须在诸如以下的协程中调用它:

GlobalScope.launch {
  abc()
}