Sobes.tech
Middle

Hoe coroutines in Kotlin-projecten te configureren en te gebruiken?

sobes.tech AI

Antwoord van AI

Om de coroutines te configureren, moet je de juiste afhankelijkheid toevoegen aan het build.gradle-bestand.

// build.gradle (app module)
dependencies {
    //... andere afhankelijkheden
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // Voor JVM, Android, JS, Native
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // Voor Android (als je Dispatchers.Main nodig hebt)
}

Het gebruik van coroutines omvat:

  1. Scope: Het bepalen van het gebied waarin coroutines worden uitgevoerd en hun levenscyclus beheren.

    • GlobalScope: Het hele applicatiegebied, voorzichtig gebruiken vanwege mogelijk geheugenlek.
    • CoroutineScope: Expliciet aangemaakt, stelt je in staat om het annuleren van coroutines te controleren.
    • Vooraf gedefinieerde scopes in frameworks (bijvoorbeeld lifecycleScope in Android ViewModel).
    // Voorbeeld met CoroutineScope
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Scope met Dispatcher en Job
    
    fun performBackgroundTask() {
        myScope.launch { // Start een coroutine
            // Lange berekening
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Annuleer alle coroutines in de Scope
    }
    
  2. Coroutine builders: Functies om coroutines te starten.

    • launch: Start een coroutine die geen resultaat expliciet retourneert (Job).
    • async: Start een coroutine die een resultaat retourneert als Deferred.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope - builder voor gestructureerde concurrency
        val data1 = async { networkCall1() } // async retourneert Deferred
        val data2 = async { networkCall2() }
    
        // Verwerking van gegevens na ontvangst
        data1.await() // Resultaat ophalen van Deferred, coroutine wordt gepauzeerd tot voltooiing
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Simulatie van netwerkverzoek
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking - builder om huidige thread te blokkeren
        launch { // launch geeft Job terug
            println("Fetching data...")
            fetchData()
            println("Data fetched!")
        }
    }
    
  3. Dispatchers: Bepalen op welke thread of threadpool de coroutine wordt uitgevoerd.

    • Dispatchers.Default: Voor CPU-intensieve taken.
    • Dispatchers.IO: Voor blokkerende I/O-operaties (bestands- en netwerktoegang).
    • Dispatchers.Main: Hoofdthread (alleen voor Android en Swing/JavaFX).
    • Dispatchers.Unconfined: Start de coroutine in de oproepende thread, slaapt, en wordt hervat in de thread die hervat.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Dispatcher specificatie
            // Voer I/O-operatie uit
            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) // Slaap
             println("Unconfined thread 2: ${Thread.currentThread().name}") // Hervatting op andere thread (mogelijk)
        }
        delay(200) // Wacht op het voltooien van coroutines
    }
    
  4. Suspending functies: Functies gemarkeerd met suspend, die kunnen worden gepauzeerd en hervat zonder het thread te blokkeren. Alleen aan te roepen vanuit andere suspending functies of binnen een coroutineblok.

  5. Annulering en uitzonderingen: Coroutines ondersteunen gestructureerde annulering en propagatie van uitzonderingen.

    • Annulering:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Working $i ...")
                  delay(100) // suspend functie die annulering respecteert
              }
          } catch (e: CancellationException) {
              println("Task was cancelled!")
          } finally {
              println("Cleaning up...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Wacht een beetje
          job.cancelAndJoin() // Annuleer en wacht op voltooiing
      }
      
    • Uitzonderingen: Bij launch leidt een niet-afgevangen uitzondering tot een crash van de app (standaard in Android). Bij async wordt de uitzondering gegooid bij await(). Voor fijnmazige controle worden CoroutineExceptionHandler of SupervisorJob gebruikt.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught exception: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Toepassing handler
            throw IllegalStateException("Something went wrong")
        }
        job.join() // Wachten op voltooiing
    }