【问题标题】:Kotlin Coroutine wait for Retrofit ResponseKotlin 协程等待改造响应
【发布时间】:2020-02-22 16:20:52
【问题描述】:

我正在尝试将 Android MVVM 模式与存储库类一起使用,并将 Retrofit 用于网络调用。我有一个常见的问题,就是无法让协程等待网络响应返回。

这个方法在我的ViewModel类中:

private fun loadConfigModel() {
    val model = runBlocking {
        withContext(Dispatchers.IO) {
            configModelRepository.getConfigFile()
        }
    }
    configModel.value = model
}

ConfigModelRepository,我有这个:

suspend fun getConfigFile(): ConfigModel {
    val configString = prefs.getString(
        ConfigViewModel.CONFIG_SHARED_PREF_KEY, "") ?: ""

    return if (configString.isEmpty() || isCacheExpired()) {
        runBlocking { fetchConfig() }
    } else {
        postFromLocalCache(configString)
    }
}

private suspend fun fetchConfig(): ConfigModel {
    return suspendCoroutine { cont ->
         dataService
            .config()  // <-- LAST LINE CALLED
            .enqueue(object : Callback<ConfigModel> {
                 override fun onResponse(call: Call<ConfigModel>, response: Response<ConfigModel>) {
                    if (response.isSuccessful) {
                        response.body()?.let {
                            saveConfigResponseInSharedPreferences(it)
                            cont.resume(it)
                        }
                    } else {
                        cont.resume(ConfigModel(listOf(), listOf()))
                    }
                }

                override fun onFailure(call: Call<ConfigModel>, t: Throwable) {
                    Timber.e(t, "config fetch failed")
                    cont.resume(ConfigModel(listOf(), listOf()))
                }
            })
    }
}

我的代码运行到dataService.config()。它永远不会进入onResponseonFailure。网络调用正确地进行并返回(我可以使用 Charles 看到这一点),但协程似乎没有在监听回调。

所以,我的问题是通常的问题。如何让协程阻塞,以便它们等待来自Retrofit 的回调?谢谢。

【问题讨论】:

  • 你可以用suspend标记你的Retrofit API调用并让它返回ConfigModel,而不是使用旧的回调机制和suspendCoroutine

标签: android kotlin retrofit2 kotlin-coroutines


【解决方案1】:

问题一定是response.body() 返回null,因为这是唯一缺少对cont.resume() 的调用的情况。确保在这种情况下也调用cont.resume(),您的代码至少应该不会卡住。

但就像 CommonsWare 指出的那样,更好的办法是升级到 Retrofit 2.6.0 或更高版本并使用原生 suspend 支持,而不是滚动您自己的 suspendCoroutine 逻辑。

您还应该完全停止使用runBlocking。在第一种情况下,launch(Dispatchers.Main) 是一个协程,并将configModel.value = model 移动到其中。在第二种情况下,您可以删除runBlocking 并直接调用fetchConfig()

【讨论】:

  • 我之前尝试过,但失败了。你的回答让我知道我只在 Retrofit 2.4 上。感谢您的建议!
猜你喜欢
  • 2016-08-02
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 2019-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多