Kotlin Coroutine等待改造响应

时间:2019-10-26 17:58:45

标签: android kotlin retrofit2 kotlin-coroutines

我正在尝试将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的回调?谢谢。

1 个答案:

答案 0 :(得分:2)

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

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

您还应该完全停止使用runBlocking。在第一种情况下,改为使用launch(Dispatchers.Main)一个协程并将configModel.value = model移入其中。在第二种情况下,您只需删除runBlocking并直接致电fetchConfig()