Назад к вопросам
Middle
236
questionbank

Как настраивать и использовать корутины в проектах на Kotlin?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Для настройки корутин необходимо добавить соответствующую зависимость в build.gradle файл.

// build.gradle (app module)
dependencies {
    //... другие зависимости
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // Для JVM, Android, JS, Native
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // Для Android (если нужен Dispatchers.Main)
}

Использование корутин включает:

  1. Область жизни (Scope): Определение области, в которой корутины будут выполняться и управлять своим жизненным циклом.

    • GlobalScope: Область жизни всего приложения, использовать осторожно, так как утечка памяти возможна.
    • CoroutineScope: Создается явно, позволяет контролировать отмену корутин.
    • Предопределенные области в фреймворках (например, lifecycleScope в Android ViewModel).
    // Пример с CoroutineScope
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Создание Scope с Dispatcher и Job
    
    fun performBackgroundTask() {
        myScope.launch { // Запуск корутины
            // Долгий вычислительный процесс
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Отмена всех корутин в Scope
    }
    
  2. Создатели корутин (Builders): Функции для запуска корутин.

    • launch: Запускает корутину, которая не возвращает результат явно (возвращает Job).
    • async: Запускает корутину, которая возвращает результат в виде Deferred.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope - builder для структурированной конкурентности
        val data1 = async { networkCall1() } // async возвращает Deferred
        val data2 = async { networkCall2() }
    
        // ... обработка данных после их получения
        data1.await() // Получение результата от Deferred, вызывает усыпление корутины до завершения
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Имитация сетевого запроса
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking - builder для блокирования текущего потока (для main или тестов)
        launch { // launch возвращает Job
            println("Fetching data...")
            fetchData()
            println("Data fetched!")
        }
    }
    
  3. Диспетчеры (Dispatchers): Определяют, в каком потоке или пуле потоков будет выполняться корутина.

    • Dispatchers.Default: Для CPU-интенсивных задач.
    • Dispatchers.IO: Для блокирующих I/O операций (файловый доступ, сеть).
    • Dispatchers.Main: Основной поток (только для Android и Swing/JavaFX).
    • Dispatchers.Unconfined: Запускает корутину в вызывающем потоке, усыпляется, а затем возобновляется в потоке, который возобновил выполнение.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Указание диспетчера
            // Выполнение I/O операции
            println("Running on thread: ${Thread.currentThread().name}")
        }
    }
    
    fun main() = runBlocking {
        launch(Dispatchers.Default) {
            println("Default thread: ${Thread.currentThread().name}")
        }
        launch(Dispatchers.Unconfined) {
             println("Unconfined thread 1: ${Thread.currentThread().name}")
             delay(100) // Усыпление
             println("Unconfined thread 2: ${Thread.currentThread().name}") // Возобновление в другом потоке (возможно)
        }
        delay(200) // Ждем завершения корутин
    }
    
  4. Приостанавливающие функции (Suspending functions): Функции, помеченные ключевым словом suspend, которые могут быть приостановлены и возобновлены без блокирования потока. Могут вызываться только из других приостанавливающих функций или из блока корутины.

  5. Обработка отмены и исключений: Корутины поддерживают структурированную отмену и распространение исключений.

    • Отмена:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Working $i ...")
                  delay(100) // suspend function that respects cancellation
              }
          } catch (e: CancellationException) {
              println("Task was cancelled!")
          } finally {
              println("Cleaning up...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Ждем немного
          job.cancelAndJoin() // Отменяем и ждем завершения
      }
      
    • Исключения: В случае launch, неперехваченное исключение приведет к падению приложения (в Android по умолчанию). В случае async, исключение будет брошено при вызове await(). Для более гранулярного контроля используются CoroutineExceptionHandler или SupervisorJob.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught exception: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Применение обработчика
            throw IllegalStateException("Something went wrong")
        }
        job.join() // Ждем завершения
    }