Приостановка сопрограммы не является функцией, которая волшебным образом «разблокирует» существующий блокирующий сетевой вызов.Это строго кооперативная функция, которая требует, чтобы код явно вызывал suspendCancellableCoroutine
.Поскольку вы используете какой-то ранее существующий блокирующий API ввода-вывода, сопрограмма блокирует вызывающий поток.
Чтобы по-настоящему использовать возможности приостанавливаемого кода, вы должны использовать неблокирующий API ввода-вывода, который позволяет сделатьзапросить и предоставить обратный вызов, который API вызовет, когда будет готов результат.Например:
NonBlockingHttp.sendRequest("https://example.org/document",
onSuccess = { println("Received document $it") },
onFailure = { Log.e("Failed to fetch the document", it) }
)
С этим видом API ни один поток не будет заблокирован, независимо от того, используете вы сопрограммы или нет.Однако по сравнению с блокирующим API его использование довольно громоздко и грязно.В этом вам помогут сопрограммы: они позволяют вам продолжать писать код точно такой же формы, как если бы он блокировал, только это не так.Чтобы получить его, вы должны сначала написать suspend fun
, который переводит ваш API в приостановку сопрограммы:
suspend fun fetchDocument(url: String): String = suspendCancellableCoroutine { cont ->
NonBlockingHttp.sendRequest(url,
onSuccess = { cont.resume(it) },
onFailure = { cont.resumeWithException(it) }
)
}
Теперь ваш код вызова возвращается к следующему:
try {
val document = fetchDocument("https://example.org/document")
println("Received document $document")
} catch (e: Exception) {
Log.e("Failed to fetch the document", e)
}
Если вместо этого у вас все в порядке с сохранением блокирующего сетевого ввода-вывода, а это означает, что вам нужен выделенный поток для каждого одновременного сетевого вызова, то без сопрограмм вам придется использовать что-то вроде асинхронной задачи, bg
от Anko и т. Д.Эти подходы также требуют предоставления обратных вызовов, поэтому сопрограммы могут снова помочь вам сохранить естественную модель программирования.Базовая библиотека сопрограмм уже поставляется со всеми необходимыми деталями:
- Специализированный пул эластичных потоков, который всегда запускает новый поток, если все в настоящий момент заблокированы (доступ через
Dispatchers.IO
) - Примитив
withContext
, который позволяет вашей сопрограмме перепрыгивать из одного потока в другой и обратно
С помощью этих инструментов вы можете просто написать
try {
val document = withContext(Dispatchers.IO) {
JSoup.connect("https://example.org/document").get()
}
println("Received document $it")
} catch (e: Exception) {
Log.e("Failed to fetch the document")
}
Когда прибудет ваша сопрограммапри вызове JSoup он освободит поток пользовательского интерфейса и выполнит эту строку в потоке в пуле потоков ввода-вывода.Когда он разблокируется и получит результат, сопрограмма вернется к потоку пользовательского интерфейса.