【问题标题】:Suspend Coroutine Hangs暂停协程挂起
【发布时间】:2019-10-13 03:19:24
【问题描述】:

尝试更深入地了解协程。我有一个应该获取网络响应的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 所需的内容。这似乎是它挂的地方。

【问题讨论】:

  • 你使用什么版本的 kotlinx-coroutines-core?您的代码无法编译,例如1.2.0 具有以下简历签名:CancellableContinuation.resume(value: T, onCancellation: (cause: Throwable) -&gt; Unit)。也许您在该库的早期版本中遇到了在更高版本中修复的错误;尝试适应并升级到这个库的最新版本。
  • 1.3.0.我向你保证,我的代码编译并且应用程序运行。至少到它挂起的地步。 :-)
  • 奇怪的是它为你编译,因为我可以在 Kotlin Playground 中重现编译错误:pl.kotl.in/xlX8nRuAK

标签: android kotlin kotlin-coroutines


【解决方案1】:

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

  1. 如果响应成功但未提供正文,则不调用任何内容。
  2. 如果响应成功,body 不为空,但it.result 不是RESULT_SUCCESS 你什么都不调用。
  3. 如果onFailure 出现问题,您什么也不叫。

只要resumecancel都没有被调用,协程就会一直挂起,意味着挂起。

【讨论】:

  • 感谢您的回复。请参阅我对 OP 所做的编辑。是否需要在 VisualElementsService 方法中添加一些内容以将回调传递给 suspendCancellableCoroutine?
【解决方案2】:

当您使用暂停关键字时,您是在告诉该函数应该在协程 bode 内调用,例如:

suspend fun abc(){
   return
}

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

GlobalScope.launch {
  abc()
}

【讨论】:

  • 谢谢。但是我如何使用可取消的协程来做到这一点?这就是我要解决的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
  • 1970-01-01
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多