Sobes.tech
Middle

Kako konfigurisati i koristiti korutine u Kotlin projektima?

sobes.tech АИ

Одговор од АИ

За подешавање кортина потребно је додати одговараву зависност у датотеку 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 за блокирање тренутног тока (нпр. main или тестови)
        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) { // Навођење диспетчера
            // Извршавање 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) // спирајућа функција која поштује отказивање
              }
          } 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) { // Примена обработљача изузетака
            throw IllegalStateException("Something went wrong")
        }
        job.join() // Чекање на завршетак
    }