Sobes.tech
Middle

Ako nakonfigurovať a používať korutiny v projektoch Kotlin?

sobes.tech AI

Odpoveď od AI

Na nastavenie korutín je potrebné pridať príslušnú závislosť do súboru build.gradle.

// build.gradle (modul aplikácie)
dependencies {
    //... ďalšie závislosti
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // pre JVM, Android, JS, Native
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // pre Android (ak je potrebný Dispatchers.Main)
}

Použitie korutín zahŕňa:

  1. Rozsah života (Scope): Určuje oblasť, v ktorej sa korutiny budú vykonávať a spravovať svoj životný cyklus.

    • GlobalScope: Oblasť života celej aplikácie, používajte opatrne, pretože môže dôjsť ku úniku pamäte.
    • CoroutineScope: Vytvára sa explicitne, umožňuje kontrolu nad zrušením korutín.
    • Preddefinované oblasti vo frameworkoch (napríklad lifecycleScope v Android ViewModel).
    // Príklad s CoroutineScope
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Vytvorenie Scope s Dispatcherom a Jobom
    
    fun performBackgroundTask() {
        myScope.launch { // Spustenie korutiny
            // Dlhý výpočtový proces
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Zrušenie všetkých korutín v Scope
    }
    
  2. Tvorcovia korutín (Builders): Funkcie na spustenie korutín.

    • launch: Spúšťa korutinu, ktorá nevracia výsledok explicitne (Job).
    • async: Spúšťa korutinu, ktorá vracia výsledok vo forme Deferred.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope - builder pre štruktúrovanú konkurenciu
        val data1 = async { networkCall1() } // async vracia Deferred
        val data2 = async { networkCall2() }
    
        // ... spracovanie dát po ich získaní
        data1.await() // Získanie výsledku z Deferred, korutina sa uspí do dokončenia
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Imitačný sieťový požiadavok
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking - builder na blokovanie aktuálneho vlákna (napr. main alebo testy)
        launch { // launch vracia Job
            println("Fetching data...")
            fetchData()
            println("Data fetched!")
        }
    }
    
  3. Dispečery (Dispatchers): Určujú, na ktorom vlákne alebo vláknovom poole bude korutina vykonávaná.

    • Dispatchers.Default: Pre CPU-intenzívne úlohy.
    • Dispatchers.IO: Pre blokujúce I/O operácie (súbory, sieť).
    • Dispatchers.Main: Hlavné vlákno (len pre Android a Swing/JavaFX).
    • Dispatchers.Unconfined: Spúšťa korutinu v volajúcom vlákne, uspí sa a potom pokračuje vo vlákne, ktoré ju obnovilo.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Určenie dispečera
            // Vykonanie I/O operácie
            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) // Uspanie
             println("Unconfined thread 2: ${Thread.currentThread().name}") // Obnovenie v inom vlákne (možno)
        }
        delay(200) // Čakanie na dokončenie korutín
    }
    
  4. Funkcie na pozastavenie (Suspending functions): Funkcie označené kľúčovým slovom suspend, ktoré môžu byť pozastavené a obnovené bez blokovania vlákna. Môžu byť volané iba z iných pozastaviteľných funkcií alebo z bloku korutiny.

  5. Spracovanie zrušenia a výnimiek: Korutiny podporujú štruktúrované zrušenie a šírenie výnimiek.

    • Zrušenie:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Working $i ...")
                  delay(100) // suspend funkcia, ktorá rešpektuje zrušenie
              }
          } catch (e: CancellationException) {
              println("Task was cancelled!")
          } finally {
              println("Cleaning up...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Čakanie
          job.cancelAndJoin() // Zrušenie a čakanie na dokončenie
      }
      
    • Výnimky: V prípade launch, nezachytené výnimky povedú k pádu aplikácie (predvolene v Android). V prípade async, výnimka bude vyhodená pri volaní await(). Pre jemnejšiu kontrolu sa používajú CoroutineExceptionHandler alebo SupervisorJob.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught exception: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Použitie spracovateľa výnimiek
            throw IllegalStateException("Something went wrong")
        }
        job.join() // Čakanie na dokončenie
    }