Sobes.tech
Middle

Hogyan állítsuk be és használjuk a korutinokat Kotlin projektekben?

sobes.tech MI

Válasz az MI-től

A korutinok beállításához megfelelő függőséget kell hozzáadni a build.gradle fájlhoz.

// build.gradle (alkalmazás modul)
dependencies {
    //... egyéb függőségek
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // JVM, Android, JS, Native
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // Androidhoz (ha szükséges Dispatchers.Main)
}

A korutinok használata magában foglalja:

  1. Életciklus (Scope): Meghatározza azt a területet, ahol a korutinok futnak és kezelik életciklusukat.

    • GlobalScope: Az alkalmazás teljes életciklusa, óvatosan használandó, mert memória szivárgáshoz vezethet.
    • CoroutineScope: Egyértelműen létrehozva, lehetővé teszi a korutinok leállítását.
    • Előre definiált területek keretrendszerekben (pl. lifecycleScope Android ViewModel-ben).
    // Példa CoroutineScope használatára
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Scope létrehozása Dispatcherrel és Jobbal
    
    fun performBackgroundTask() {
        myScope.launch { // Korutina indítása
            // Hosszú számítási folyamat
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Minden korutina leállítása a Scope-ban
    }
    
  2. Korutin készítők (Builders): Funkciók a korutinok indítására.

    • launch: Indít egy korutint, amely nem ad vissza eredményt (Job-t ad vissza).
    • async: Indít egy korutint, amely eredményt ad vissza Deferred formájában.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope a strukturált konkurencia számára
        val data1 = async { networkCall1() } // async Deferred-et ad vissza
        val data2 = async { networkCall2() }
    
        // ... adatok feldolgozása az eredmények után
        data1.await() // Eredmény lekérése a Deferred-ből, a korutina alvásba kerül addig, amíg nem kész
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Hálózati kérés szimulálása
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking a blokkoló builder a jelenlegi szálon (pl. main vagy tesztek)
        launch { // launch Job-t ad vissza
            println("Fetching data...")
            fetchData()
            println("Data fetched!")
        }
    }
    
  3. Dispatcherek (Dispatchers): Meghatározzák, hogy melyik szálon vagy szálpufferben fusson a korutina.

    • Dispatchers.Default: CPU-intenzív feladatokhoz.
    • Dispatchers.IO: Blokkoló I/O műveletekhez (fájl hozzáférés, hálózat).
    • Dispatchers.Main: Fő szál (csak Android és Swing/JavaFX esetén).
    • Dispatchers.Unconfined: A hívó szálon indítja a korutint, majd alvásba kerül, és később a folytatás szálán folytatódik.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Dispatcher megadása
            // I/O művelet végrehajtása
            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) // Alvás
             println("Unconfined thread 2: ${Thread.currentThread().name}") // Folytatás más szálon (lehetséges)
        }
        delay(200) // Várakozás a korutinok befejezésére
    }
    
  4. Szüneteltető függvények (Suspending functions): A suspend kulcsszóval ellátott függvények, amelyek szüneteltethetők és folytathatók anélkül, hogy blokkolnák a szálat. Csak más szüneteltető függvényekből vagy korutin blokkokból hívhatók.

  5. Megszakítás és kivételek kezelése: A korutinok támogatják a strukturált megszakítást és a kivételek terjesztését.

    • Megszakítás:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Working $i ...")
                  delay(100) // szüneteltető függvény, amely figyelembe veszi a megszakítást
              }
          } catch (e: CancellationException) {
              println("Task was cancelled!")
          } finally {
              println("Cleaning up...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Várakozás
          job.cancelAndJoin() // Megszakítás és várakozás a befejezésre
      }
      
    • Kivétel: A launch esetén a kezeletlen kivétel a program leállásához vezet (Androidon alapértelmezés szerint). A async esetén a kivétel a await() hívásakor dobódik. A finomabb kontroll érdekében használhatók a CoroutineExceptionHandler vagy a SupervisorJob.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught exception: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Kivételkezelő alkalmazása
            throw IllegalStateException("Something went wrong")
        }
        job.join() // Befejezés várása
    }