Kotlin Coroutine ждет ответа на модернизацию - PullRequest
0 голосов
/ 26 октября 2019

Я пытаюсь использовать шаблон 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. Сетевой вызов идет и возвращается должным образом (я вижу это с помощью Чарльза), но сопрограмма, похоже, не ожидает обратного вызова.

Итак, мой вопрос обычный. Как я могу заставить сопрограммы блокировать так, чтобы они ждали этот обратный вызов от Retrofit? Спасибо.

1 Ответ

2 голосов
/ 26 октября 2019

Проблема должна заключаться в том, что response.body() возвращает null, поскольку это единственный случай, когда пропущен вызов cont.resume(). Обязательно вызовите cont.resume() также и в этом случае, и ваш код по крайней мере не должен застрять.

Но, как указывает CommonsWare, еще лучше было бы перейти на Retrofit 2.6.0 или новее и использовать native *Поддержка 1007 * вместо использования собственной логики suspendCoroutine.

Вам также следует полностью прекратить использование runBlocking. В первом случае launch(Dispatchers.Main) вместо сопрограммы и переместите configModel.value = model в нее. Во втором случае вы можете просто удалить runBlocking и позвонить fetchConfig() напрямую.

...