Sobes.tech
Middle

Jak nakonfigurovat a používat korutiny v projektech Kotlin?

sobes.tech AI

Odpověď od AI

Pro nastavení korutin je třeba přidat odpovídající závislost do souboru build.gradle.

// build.gradle (modul aplikace)
dependencies {
    //... další závislosti
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // pro JVM, Android, JS, Native
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // pro Android (pokud je potřeba Dispatchers.Main)
}

Použití korutin zahrnuje:

  1. Rozsah života (Scope): Určuje oblast, ve které se korutiny budou vykonávat a spravovat svůj životní cyklus.

    • GlobalScope: Životní cyklus celé aplikace, používejte opatrně, protože může dojít ke ztrátě paměti.
    • CoroutineScope: Vytvořen explicitně, umožňuje kontrolu zrušení korutin.
    • Předdefinované oblasti ve frameworkách (například lifecycleScope v Android ViewModel).
    // Příklad s CoroutineScope
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Vytvoření Scope s Dispatcherem a Jobem
    
    fun performBackgroundTask() {
        myScope.launch { // Spuštění korutiny
            // Dlouhý výpočetní proces
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Zrušení všech korutin v Scope
    }
    
  2. Tvořitelé korutin (Builders): Funkce pro spuštění korutin.

    • launch: Spouští korutinu, která explicitně nevrací výsledek (Job).
    • async: Spouští korutinu, která vrací výsledek ve formě Deferred.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope - builder pro strukturovanou konkurenci
        val data1 = async { networkCall1() } // async vrací Deferred
        val data2 = async { networkCall2() }
    
        // ... zpracování dat po jejich získání
        data1.await() // Získání výsledku z Deferred, korutina se uspí do dokončení
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Simulace síťového požadavku
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking - builder blokující aktuální vlákno (např. main nebo testy)
        launch { // launch vrací Job
            println("Fetching data...")
            fetchData()
            println("Data fetched!")
        }
    }
    
  3. Dispečery (Dispatchers): Určují, na kterém vlákně nebo vláknovém poolu bude korutina vykonávána.

    • Dispatchers.Default: Pro CPU-intenzivní úkoly.
    • Dispatchers.IO: Pro blokující I/O operace (soubory, síť).
    • Dispatchers.Main: Hlavní vlákno (pouze pro Android a Swing/JavaFX).
    • Dispatchers.Unconfined: Spouští korutinu ve volajícím vlákně, uspí se a poté pokračuje ve vlákně, které obnovilo její běh.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Určení dispečera
            // Provádění I/O operace
            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) // Uspání
             println("Unconfined thread 2: ${Thread.currentThread().name}") // Obnovení na jiném vlákně (možné)
        }
        delay(200) // Čekání na dokončení korutin
    }
    
  4. Funkce pozastavení (Suspending functions): Funkce označené klíčovým slovem suspend, které mohou být pozastaveny a obnoveny bez blokování vlákna. Mohou být volány pouze z jiných pozastavitelných funkcí nebo z bloku korutiny.

  5. Zpracování zrušení a výjimek: Korutiny podporují strukturované zrušení a šíření výjimek.

    • Zrušení:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Working $i ...")
                  delay(100) // suspend funkce, která respektuje zrušení
              }
          } catch (e: CancellationException) {
              println("Task was cancelled!")
          } finally {
              println("Cleaning up...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Čekání
          job.cancelAndJoin() // Zrušení a čekání na dokončení
      }
      
    • Výjimky: V případě launch nezachycená výjimka povede k pádu aplikace (výchozí v Androidu). V případě async bude výjimka vyhozena při volání await(). Pro jemnější kontrolu lze použít CoroutineExceptionHandler nebo SupervisorJob.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught exception: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Použití zpracovatele výjimek
            throw IllegalStateException("Something went wrong")
        }
        job.join() // Čekání na dokončení
    }