Параллельные запросы с сопрограммами - PullRequest
2 голосов
/ 04 мая 2020

Я пытаюсь получить некоторые данные из нескольких мест, чтобы заполнить программу повторного просмотра. Раньше я использовал обратные вызовы, которые работали нормально, но мне нужно было перестроить их в сопрограммы.

Итак, у меня есть список сервисов по модернизации и каждый из них называется parallerl. Затем я могу обновить recyclerView с помощью обратного вызова onResponse. Как я могу добиться этого с сопрограммами.

Я пытался что-то подобное, но следующий вызов запускается после того, как я получил ответ:

runblocking {
    for (service in services) {
        val response = async(Dispatchers.IO) {
            service.getResponseAsync()
        }
        adapter.updateRecyclerView(response.await())
    }
}

При другом подходе у меня была проблема, что я не смог вернуться в основной поток, чтобы обновить мой пользовательский интерфейс, когда я использовал запуск, и не смог дождаться ответа:

runblocking {
    services.foreach {
        launch(Dispatcher.IO) {
            val response = it.getResponseAsync()
        }
        withContext(Dispatcher.Main) {
            adapter.updateRecyclerView(response)
        }
    }
}

Я благодарен за каждый совет;) Ура Патрик

Ответы [ 2 ]

3 голосов
/ 04 мая 2020

Запуск сопрограмм с launch вместо runBlocking. В примерах ниже предполагается, что вы запускаете из контекста, который по умолчанию использует Dispatchers.Main. Если это не так, вы можете использовать launch(Dispatchers.Main) для них.

Если вы хотите обновлять свое представление каждый раз, когда возвращается любое из параллельных действий, тогда поместите свое обновление пользовательского интерфейса в запускаемые вами сопрограммы. для каждого из service элементов:

for (service in services) {
    launch {
        val response = withContext(Dispatchers.IO) { service.getResponseAsync() }
        adapter.updateRecyclerView(response)
    }
}

Если вам нужно обновить только после того, как все они вернутся, вы можете использовать awaitAll. Здесь ваша updateRecyclerView функция должна быть написана для обработки списка ответов, а не по одному за раз.

launch {
    val responses = services.map { service ->
        async(Dispatchers.IO) { service.getResponseAsync() }
    }
    adapter.updateRecyclerView(responses.awaitAll())
}
1 голос
/ 04 мая 2020

Вызов await() приостанавливает текущую сопрограмму и освобождает текущий поток для присоединения другими подпрограммами из очереди.

Так, когда вызывается await(), текущая сопрограмма приостанавливается до получения ответа, и это почему l oop не завершается (переход к следующей итерации перед завершением запроса перед запросом).


Во-первых, здесь не следует использовать runBlocking, поэтому крайне не рекомендуется используется в рабочей среде.

Вместо этого следует использовать область ViewModel, предоставленную android для структурированного параллелизма (отменяет запрос, если он больше не нужен, например, если жизненный цикл активности закончен).

Вы можете использовать область видимости модели, подобную этой, в упражнении или фрагменте viewModelOwner.viewModelScope.launch(/*Other dispatcher if needed*/) {} или создать область сопрограммы самостоятельно с прикрепленным заданием, которое отменяет себя на onDestroy.


Для решения проблемы сопрограмма не выполняет параллельные запросы , вы можете запустить несколько запросов без ожидания (ING) на них внутри для l oop. * 1 017 *

И выберите их, используя выражение выбора https://kotlinlang.org/docs/reference/coroutines/select-expression.html#selecting -deferred-values ​​

Пример:

viewModelOwner.viewModelScope.launch {
    val responses = mutableListOf<Deferred<TypeReturnedFromGetResponse>>()
    for (service in services) {
        async(Dispatchers.IO) {
            service.getResponseAsync()
        }.let(responses::add)
    }

    // adds which ever request is done first in oppose to awaiting for all then update
    for (i in responses.indices) {
        select<Unit> {
            for (response in responses) {
                response.onAwait {
                    adapter.updateRecyclerView(it)
                }
            }
        }
    }
}

PS: использование этого метода выглядит некрасиво, но обновит адаптер, как только будет решен первый запрос, вместо того, чтобы ждать каждого запроса и затем обновлять элементы в нем.

...