我正在尝试将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()
。它永远不会输入onResponse
或onFailure
。网络调用可以反复发送并正确返回(我可以使用Charles看到此消息),但是协程似乎没有监听回调。
所以,我的问题是通常的问题。我如何才能阻止协程以使它们等待来自Retrofit
的回调?谢谢。
答案 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()
。