Сопрограммы позволяют писать асинхронный код более последовательным и читаемым образом, упрощая работу с фоновыми задачами, такими как сетевые запросы, операции с базами данных и длительные вычисления, без блокировки основного потока (UI).
Конструкторы сопрограмм
Построители сопрограмм — это функции, которые создают сопрограммы. Существует множество различных конструкторов сопрограмм, каждый из которых имеет свою собственную цель.
1. запуск: этот конструктор идеально подходит для запуска новой сопрограммы, которая не возвращает результат. Он идеально подходит для задач «выстрелил и забыл», таких как выполнение фоновых операций или обновление пользовательского интерфейса.
CoroutineScope(Dispatchers.IO).launch { // Perform a background task }
2. async: используйте async
для запуска сопрограммы, которая возвращает значение Deferred
, что позволяет вам получить результат асинхронной операции без блокировки с помощью await()
.
val deferredResult = CoroutineScope(Dispatchers.IO).async { // Perform an asynchronous task and return a result } val result = deferredResult.await()
3. runBlocking: хотя runBlocking
обычно используется в модульных тестах и основных функциях, его лучше избегать в коде пользовательского интерфейса Android, поскольку он может заблокировать поток пользовательского интерфейса.
runBlocking { // Perform blocking operations }
4. withContext: этот конструктор временно переключает контекст (поток) сопрограммы для выполнения определенной задачи перед возвратом в исходный контекст. Это полезно для работы в другом потоке, а затем возврата в поток пользовательского интерфейса.
val result = withContext(Dispatchers.IO) { // Perform an operation on a background thread "Result" }
Области сопрограммы
Области сопрограмм — это фундаментальная концепция сопрограмм Kotlin, которая помогает управлять жизненным циклом и отменой сопрограмм. Они предоставляют структурированный способ запуска сопрограмм и управления ими в определенной области, гарантируя, что все сопрограммы, запущенные в этой области, автоматически отменяются при отмене области. Это особенно важно для предотвращения утечек памяти и обеспечения правильной очистки фоновой работы.
1. GlobalScope: это предопределенная область действия сопрограммы, которая не привязана к какому-либо конкретному жизненному циклу или контексту. Сопрограммы, запущенные в GlobalScope, не привязаны к какому-либо конкретному жизненному циклу и продолжают работать до тех пор, пока не завершатся или не будут отменены вручную.
GlobalScope.launch { // Coroutine code in the global scope }
2. CoroutineScope: это настраиваемая область сопрограммы, которую вы можете создать для управления жизненным циклом сопрограмм в определенном контексте, например жизненном цикле компонента Android (например, активности или фрагмента) или настраиваемом контексте.
val coroutineScope = CoroutineScope(Dispatchers.IO) coroutineScope.launch { // Coroutine code within the custom scope }
Когда контекст-владелец (например, активность или фрагмент) уничтожается, вы можете отменить CoroutineScope, и все связанные с ним сопрограммы также будут отменены.
3. viewModelScope: при разработке приложений для Android вы можете использовать viewModelScope, предоставляемый библиотеками AndroidX, такими как androidx.lifecycle.viewModelScope. Он привязан к жизненному циклу ViewModel и автоматически отменяет сопрограммы при очистке ViewModel.
class MyViewModel : ViewModel() { fun performTask() { viewModelScope.launch { // Coroutine code within the viewModelScope } } }
Когда ViewModel больше не нужен, его viewModelScope автоматически отменяется.
4. lifecycleScope: это еще одна область действия, специфичная для Android, предоставляемая библиотеками AndroidX, такими как androidx.lifecycle.lifecycleScope. Он привязан к жизненному циклу компонента Android (например, активности или фрагмента) и отменяет сопрограммы при уничтожении компонента.
class MyFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) lifecycleScope.launch { // Coroutine code within the lifecycleScope } } }
Когда компонент Android (например, фрагмент) уничтожается, его жизненный цикл автоматически отменяется.
5. SupervisorScope: особенно полезен, когда вы хотите изолировать и обрабатывать ошибки таким образом, чтобы не нарушать всю иерархию сопрограмм. Это обеспечивает более надежную и детальную обработку ошибок в ваших сопрограммах.
Вот пример использования SupervisorScope:
fun main() = runBlocking { supervisorScope { val child1 = launch { try { // Perform a task that might throw an exception delay(100) throw RuntimeException("Child 1 failed") } catch (e: Exception) { println("Child 1: Caught an exception: ${e.message}") } } val child2 = launch { // Another child coroutine delay(200) println("Child 2: Completed successfully") } // Wait for child coroutines to finish child1.join() child2.join() } println("Parent: Completed") }
В этом примере:
Мы создаем область супервизора для запуска двух дочерних сопрограмм, child1 и child2.
child1 намеренно генерирует исключение, но мы перехватываем его внутри сопрограммы и обрабатываем, не затрагивая другие сопрограммы.
child2 завершается успешно, и его результат на него не влияет сбой дочерней программы1.
Родительская сопрограмма, содержащая супервизор, печатает «Родитель: завершено» после завершения дочерней сопрограммы.
Диспетчеры сопрограмм
Диспетчеры сопрограмм в Котлине — это объекты, которые контролируют запуск сопрограмм. Сопрограммы могут выполняться в основном потоке, фоновом потоке или в пользовательском пуле потоков.
- Dispatchers.Main: этот диспетчер запускает сопрограммы в основном потоке.
- Dispatchers.IO: этот диспетчер запускает сопрограммы в фоновом потоке, оптимизированном для операций ввода-вывода.
- Dispatchers.Default: этот диспетчер запускает сопрограммы в фоновом потоке, оптимизированном для операций с интенсивным использованием ЦП.
- Dispatchers.Unconfined: этот диспетчер запускает сопрограммы в потоке, который их создал.
- Dispatchers.UnboundedPool: этот диспетчер запускает сопрограммы в пуле потоков, который может увеличиваться и уменьшаться по мере необходимости.
Некоторые полезные функции сопрограмм
- suspendCoroutineФункция suspendCoroutine полезна для написания асинхронного кода, выполнение которого можно прервать и возобновить позже. Например, вы можете использовать suspendCoroutine для написания функции, которая вызывает API, а затем возобновляет работу сопрограммы при получении ответа.
suspend fun fetchData(): String = suspendCoroutine { continuation -> // Simulate an asynchronous operation with a callback fetchDataFromNetwork { result, error -> if (error != null) { // If there's an error, resume with an exception continuation.resumeWithException(error) } else { // If successful, resume with the result continuation.resume(result) } } }
- withTimeoutOrNull: функция withTimeoutOrNull в Kotlin — это функция приостановки, которая запускает сопрограмму с тайм-аутом. Если сопрограмма не завершается до истечения времени ожидания, функция withTimeoutOrNull возвращает значение null.
fun main(args: Array<String>) { // Create a coroutine scope. withContext(Dispatchers.Main) { // Run a coroutine with a timeout of 1 second. val result = withTimeoutOrNull(1000L) { // Do some work that takes more than 1 second. delay(2000) "Hello, world!" } // Check the result of the coroutine. if (result != null) { println(result) } else { println("The coroutine timed out.") } } }
Удачной сопрограммы!!!