Sobes.tech
Middle

Cum să configurezi și să utilizezi corutinele în proiecte Kotlin?

sobes.tech AI

Răspuns de la AI

Pentru configurarea corutinelor, trebuie să adăugați dependența corespunzătoare în fișierul build.gradle.

// build.gradle (modulul aplicației)
dependencies {
    //... alte dependențe
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // Pentru JVM, Android, JS, Native
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // Pentru Android (dacă aveți nevoie de Dispatchers.Main)
}

Utilizarea corutinelor include:

  1. Domeniu de viață (Scope): Stabilirea domeniului în care vor fi executate corutinele și gestionarea ciclului lor de viață.

    • GlobalScope: Domeniul întregii aplicații, folosiți cu precauție, deoarece poate duce la scurgeri de memorie.
    • CoroutineScope: Creat explicit, permite controlul anulării corutinelor.
    • Domenii predefinite în cadre (de exemplu, lifecycleScope în Android ViewModel).
    // Exemplu cu CoroutineScope
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Crearea unui domeniu cu Dispatcher și Job
    
    fun performBackgroundTask() {
        myScope.launch { // Pornirea unei corutine
            // Proces de calcul lung
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Anulare toate corutinele din domeniu
    }
    
  2. Constructori de corutine (Builders): Funcții pentru a porni corutine.

    • launch: Pornește o corutină care nu returnează un rezultat explicit (Job).
    • async: Pornește o corutină care returnează un rezultat sub forma Deferred.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope - builder pentru concurență structurată
        val data1 = async { networkCall1() } // async returnează Deferred
        val data2 = async { networkCall2() }
    
        // ... procesare date după primire
        data1.await() // Obține rezultatul din Deferred, pune corutina în așteptare până se finalizează
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Simulare cerere de rețea
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking - builder pentru blocarea thread-ului curent
        launch { // launch returnează Job
            println("Fetching data...")
            fetchData()
            println("Data fetched!")
        }
    }
    
  3. Dispatchers: Determină pe ce thread sau pool de thread-uri va fi executată corutina.

    • Dispatchers.Default: Pentru sarcini intensive CPU.
    • Dispatchers.IO: Pentru operații I/O blocante (fișiere, rețea).
    • Dispatchers.Main: Thread principal (doar pentru Android și Swing/JavaFX).
    • Dispatchers.Unconfined: Pornește corutina în thread-ul apelant, se pune în așteptare și apoi se reia în thread-ul care a reluat execuția.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Specificarea dispatcher-ului
            // Executarea operației 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) // Punere în așteptare
             println("Unconfined thread 2: ${Thread.currentThread().name}") // Reîncepere în alt thread (posibil)
        }
        delay(200) // Așteptarea finalizării corutinelor
    }
    
  4. Funcții de suspendare (Suspending functions): Funcții marcate cu suspend, care pot fi puse în pauză și reluate fără a bloca thread-ul. Pot fi apelate doar din alte funcții suspend sau dintr-un bloc de corutine.

  5. Gestionarea anulării și excepțiilor: Corutinele suportă anulare structurată și propagarea excepțiilor.

    • Anulare:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Working $i ...")
                  delay(100) // funcție suspend care respectă anularea
              }
          } catch (e: CancellationException) {
              println("Task was cancelled!")
          } finally {
              println("Cleaning up...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Așteptare
          job.cancelAndJoin() // Anulare și așteptare finalizare
      }
      
    • Excepții: În cazul launch, excepția necapturată va duce la prăbușirea aplicației (implicit în Android). În cazul async, excepția va fi aruncată la apelul await(). Pentru control mai fin, se folosesc CoroutineExceptionHandler sau SupervisorJob.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught exception: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Aplicarea handler-ului
            throw IllegalStateException("Something went wrong")
        }
        job.join() // Așteptarea finalizării
    }