Sobes.tech
Middle

Πώς να ρυθμίσετε και να χρησιμοποιήσετε τις coroutine σε έργα Kotlin;

sobes.tech AI

Απάντηση από AI

Για τη ρύθμιση των κορουτινών, πρέπει να προσθέσετε την κατάλληλη εξάρτηση στο αρχείο build.gradle.

// build.gradle (τμήμα εφαρμογής)
dependencies {
    //... άλλες εξαρτήσεις
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // Για JVM, Android, JS, Native
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // Για Android (αν χρειάζεται Dispatchers.Main)
}

Η χρήση των κορουτινών περιλαμβάνει:

  1. Περιοχή ζωής (Scope): Ορισμός της περιοχής στην οποία θα εκτελούνται οι κορουτίνες και διαχείριση του κύκλου ζωής τους.

    • GlobalScope: Περιοχή όλης της εφαρμογής, χρησιμοποιήστε με προσοχή, καθώς μπορεί να οδηγήσει σε διαρροές μνήμης.
    • CoroutineScope: Δημιουργείται ρητά, επιτρέπει τον έλεγχο ακύρωσης των κορουτινών.
    • Προκαθορισμένες περιοχές σε πλαίσια (π.χ., lifecycleScope στο Android ViewModel).
    // Παράδειγμα με CoroutineScope
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Δημιουργία Scope με Dispatcher και Job
    
    fun performBackgroundTask() {
        myScope.launch { // Εκκίνηση κορουτίνας
            // Μακράς διάρκειας υπολογιστική διαδικασία
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Ακύρωση όλων των κορουτινών στο Scope
    }
    
  2. Κατασκευαστές κορουτινών (Builders): Συναρτήσεις για την εκκίνηση κορουτινών.

    • launch: Ξεκινά μια κορουτίνα που δεν επιστρέφει αποτέλεσμα ρητά (Job).
    • async: Ξεκινά μια κορουτίνα που επιστρέφει αποτέλεσμα ως Deferred.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope - builder για δομημένο ανταγωνισμό
        val data1 = async { networkCall1() } // async επιστρέφει Deferred
        val data2 = async { networkCall2() }
    
        // ... επεξεργασία δεδομένων μετά την λήψη
        data1.await() // Λήψη αποτελέσματος από Deferred, θέτει την κορουτίνα σε αναμονή μέχρι να ολοκληρωθεί
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Προσομοίωση δικτυακού αιτήματος
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking - builder για μπλοκάρισμα του τρέχοντος νήματος
        launch { // launch επιστρέφει Job
            println("Fetching data...")
            fetchData()
            println("Data fetched!")
        }
    }
    
  3. Dispatchers: Ορίζουν σε ποιο νήμα ή πού θα εκτελεστεί η κορουτίνα.

    • Dispatchers.Default: Για εργασίες που απαιτούν CPU.
    • Dispatchers.IO: Για μπλοκαριστικές λειτουργίες I/O (αρχεία, δίκτυο).
    • Dispatchers.Main: Κύριο νήμα (μόνο για Android και Swing/JavaFX).
    • Dispatchers.Unconfined: Ξεκινά την κορουτίνα στο καλούν νήμα, κοιμάται και συνεχίζει στο νήμα που την επανεκκινεί.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Ορισμός dispatcher
            // Εκτέλεση λειτουργίας 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) // Κοιμάται
             println("Unconfined thread 2: ${Thread.currentThread().name}") // Επανεκκίνηση σε άλλο νήμα (πιθανώς)
        }
        delay(200) // Αναμονή για ολοκλήρωση των κορουτινών
    }
    
  4. Λειτουργίες αναστολής (Suspending functions): Συναρτήσεις με το suspend, που μπορούν να ανασταλούν και να συνεχιστούν χωρίς να μπλοκάρουν το νήμα. Μπορούν να καλούνται μόνο από άλλες λειτουργίες αναστολής ή από μπλοκ κορουτίνας.

  5. Διαχείριση ακύρωσης και εξαιρέσεων: Οι κορουτίνες υποστηρίζουν δομημένη ακύρωση και διανομή εξαιρέσεων.

    • Ακύρωση:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Working $i ...")
                  delay(100) // λειτουργία suspend που σέβεται την ακύρωση
              }
          } catch (e: CancellationException) {
              println("Task was cancelled!")
          } finally {
              println("Cleaning up...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Αναμονή
          job.cancelAndJoin() // Ακύρωση και αναμονή ολοκλήρωσης
      }
      
    • Εξαιρέσεις: Στην περίπτωση launch, η μη-αντιμετωπιζόμενη εξαίρεση θα οδηγήσει σε πτώση της εφαρμογής (στο Android κατά κανόνα). Στην περίπτωση async, η εξαίρεση θα ριχθεί κατά την κλήση await(). Για πιο λεπτομερή έλεγχο, χρησιμοποιούνται CoroutineExceptionHandler ή SupervisorJob.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught exception: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Εφαρμογή handler
            throw IllegalStateException("Something went wrong")
        }
        job.join() // Αναμονή για ολοκλήρωση
    }