繁体   English   中英

暂停协程挂起

[英]Suspend Coroutine Hangs

试图更深入地了解协程。 我有一个应该获取网络响应的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
                }
            })
    }
}

这是它挂起的地方:

视觉元素服务.kt

fun fetchVisualElementsForClub(clubId: String): Call<ResultVisualElements> {
    return dataFetcherService.getVisualElementsForClub(clubId)
}

我在这里想念什么? 我试图使fetchVisualElementsForClub()成为暂停 function,但这只会使suspendCancellableCoroutine抛出一个Suspension functions can only be called within coroutine body 但我以为他在协程体内?

任何帮助表示赞赏。 谢谢。

编辑

我在下面回复 Rene 的回答,我想补充一些东西。

你是对的,我错过了三个 cont.cancel() 电话。 我已经修改了OP。 好点。

我在suspendCancellableCoroutine都有断点,这样任何可能的情况(成功、失败等)都会受到影响。 但该回调从未注册。

想知道fetchVisualElementsForClub()中是否缺少将回调传递给suspendCancellableCoroutine所需的东西。 这似乎是悬挂的地方。

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

  1. 如果响应成功但没有提供正文,则什么都不调用。
  2. 如果响应成功,body 不是 null,但是it.result不是RESULT_SUCCESS你什么都不叫。
  3. 如果onFailure出现问题,你什么也不叫。

只要没有调用resumecancel ,协程就会保持挂起状态,即挂起。

当您使用暂停关键字时,您会告诉 function 应该在协同程序中调用,例如:

suspend fun abc(){
   return
}

当你想在 function 上面调用时,你必须在协程中调用它,如下所示:

GlobalScope.launch {
  abc()
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM