Sobes.tech
Middle

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

sobes.tech AI

Отговор от AI

За настройка на корутините трябва да добавите съответната зависимост във файла build.gradle.

// build.gradle (модул приложение)
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) // спираща функция, която уважава отменянето
              }
          } 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() // Изчакване за завършване
    }