Sobes.tech
Middle

Wie man Koroutinen in Kotlin-Projekten einrichtet und verwendet?

sobes.tech KI

Antwort von AI

Um Coroutinen-Setup erfordert die Hinzufügung der entsprechenden Abhängigkeit in die build.gradle-Datei.

// build.gradle (Modul app)
dependencies {
    //... andere Abhängigkeiten
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // Für JVM, Android, JS, Native
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // Für Android (wenn Dispatcher.Main benötigt wird)
}

Die Verwendung von Coroutinen umfasst:

  1. Scope: Definition des Bereichs, in dem die Coroutinen ausgeführt werden und Verwaltung ihres Lebenszyklus.

    • GlobalScope: Anwendungsbereich, vorsichtig verwenden, da Speicherlecks möglich sind.
    • CoroutineScope: Wird explizit erstellt, ermöglicht die Kontrolle der Abbruchfunktion.
    • Vorgegebene Bereiche in Frameworks (z.B. lifecycleScope in Android ViewModel).
    // Beispiel mit CoroutineScope
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Erstellen eines Scopes mit Dispatcher und Job
    
    fun performBackgroundTask() {
        myScope.launch { // Starten einer Coroutine
            // Langer Rechenprozess
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Alle Coroutinen im Scope abbrechen
    }
    
  2. Builder: Funktionen zum Starten von Coroutinen.

    • launch: Startet eine Coroutine, die kein Ergebnis explizit zurückgibt (Job).
    • async: Startet eine Coroutine, die ein Ergebnis vom Typ Deferred zurückgibt.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope - Builder für strukturierte Konkurrenz
        val data1 = async { networkCall1() } // async gibt einen Deferred zurück
        val data2 = async { networkCall2() }
    
        // ... Verarbeitung der Daten nach Erhalt
        data1.await() // Ergebnis von Deferred holen, suspendiert die Coroutine bis zum Abschluss
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Simulation einer Netzwerkanfrage
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking - Builder zum Blockieren des aktuellen Threads (für main oder Tests)
        launch { // launch gibt einen Job zurück
            println("Daten werden abgerufen...")
            fetchData()
            println("Daten abgerufen!")
        }
    }
    
  3. Dispatcher: Bestimmen, auf welchem Thread oder Thread-Pool die Coroutine ausgeführt wird.

    • Dispatchers.Default: Für CPU-intensive Aufgaben.
    • Dispatchers.IO: Für blockierende I/O-Operationen (Dateizugriff, Netzwerk).
    • Dispatchers.Main: Haupt-Thread (nur für Android und Swing/JavaFX).
    • Dispatchers.Unconfined: Führt die Coroutine im aufrufenden Thread aus, sie wird suspendiert und dann im Thread wieder aufgenommen, der sie wieder aufnimmt.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Dispatcher angeben
            // Ausführen einer I/O-Operation
            println("Ausführung im 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) // Suspendieren
             println("Unconfined-Thread 2: ${Thread.currentThread().name}") // Fortsetzen in anderem Thread (möglich)
        }
        delay(200) // Warten auf Abschluss der Coroutinen
    }
    
  4. Suspendierende Funktionen: Funktionen, die mit suspend gekennzeichnet sind und suspendiert und wieder aufgenommen werden können, ohne den Thread zu blockieren. Können nur aus anderen suspendierenden Funktionen oder Coroutine-Blöcken aufgerufen werden.

  5. Abbruch und Fehlerbehandlung: Coroutinen unterstützen strukturierte Abbrüche und Fehlerweiterleitung.

    • Abbruch:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Arbeiten $i ...")
                  delay(100) // Suspendierende Funktion, die Abbruch respektiert
              }
          } catch (e: CancellationException) {
              println("Aufgabe wurde abgebrochen!")
          } finally {
              println("Aufräumen...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Etwas warten
          job.cancelAndJoin() // Abbrechen und auf Abschluss warten
      }
      
    • Fehler: Bei launch führt eine unbehandelte Ausnahme zum Absturz der Anwendung (bei Android standardmäßig). Bei async wird die Ausnahme beim Aufruf von await() ausgelöst. Für granularere Kontrolle werden CoroutineExceptionHandler oder SupervisorJob verwendet.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Fangene Ausnahme: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Handler anwenden
            throw IllegalStateException("Etwas ist schief gelaufen")
        }
        job.join() // Warten auf Abschluss
    }