Middle
How to set up and use coroutines in Kotlin projects?
sobes.tech AI
Answer from AI
To set up coroutines, you need to add the appropriate dependency in the build.gradle file.
// build.gradle (app module)
dependencies {
//... other dependencies
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // For JVM, Android, JS, Native
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // For Android (if you need Dispatchers.Main)
}
Using coroutines involves:
-
Scope: Defining the area in which coroutines will run and manage their lifecycle.
GlobalScope: The lifetime of the entire application, use cautiously as it can lead to memory leaks.CoroutineScope: Created explicitly, allows control over cancellation.- Predefined scopes in frameworks (e.g.,
lifecycleScopein Android ViewModel).
// Example with CoroutineScope val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Creating a Scope with Dispatcher and Job fun performBackgroundTask() { myScope.launch { // Launching a coroutine // Long computation process } } fun cancelAllTasks() { myScope.cancel() // Cancel all coroutines in the Scope } -
Coroutine Builders: Functions to launch coroutines.
launch: Starts a coroutine that does not explicitly return a result (returnsJob).async: Starts a coroutine that returns a result asDeferred.
import kotlinx.coroutines.* suspend fun fetchData() = coroutineScope { // coroutineScope - builder for structured concurrency val data1 = async { networkCall1() } // async returns Deferred val data2 = async { networkCall2() } // ... process data after retrieval data1.await() // Get result from Deferred, suspends coroutine until completion data2.await() } suspend fun networkCall1(): String { delay(1000) // Simulate network request return "Data 1" } suspend fun networkCall2(): String { delay(1500) return "Data 2" } fun startFetching() = runBlocking { // runBlocking - builder for blocking current thread (for main or tests) launch { // launch returns Job println("Fetching data...") fetchData() println("Data fetched!") } } -
Dispatchers: Define on which thread or thread pool the coroutine will run.
Dispatchers.Default: For CPU-intensive tasks.Dispatchers.IO: For blocking I/O operations (file access, network).Dispatchers.Main: Main thread (only for Android and Swing/JavaFX).Dispatchers.Unconfined: Launches coroutine in the calling thread, suspends, then resumes in the thread that resumes execution.
import kotlinx.coroutines.* fun simpleTask() { GlobalScope.launch(Dispatchers.IO) { // Specify dispatcher // Perform I/O operation 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) // Suspend println("Unconfined thread 2: ${Thread.currentThread().name}") // Resume in another thread (possibly) } delay(200) // Wait for coroutines to finish } -
Suspending functions: Functions marked with
suspendkeyword that can be suspended and resumed without blocking the thread. They can only be called from other suspending functions or within a coroutine block. -
Cancellation and exception handling: Coroutines support structured cancellation and exception propagation.
- Cancellation:
import kotlinx.coroutines.* fun cancellableTask() = GlobalScope.launch { try { repeat(1000) { i -> println("Working $i ...") delay(100) // suspend function that respects cancellation } } catch (e: CancellationException) { println("Task was cancelled!") } finally { println("Cleaning up...") } } fun main() = runBlocking { val job = cancellableTask() delay(500) // Wait a bit job.cancelAndJoin() // Cancel and wait for completion } - Exceptions: In case of
launch, unhandled exceptions will crash the app (by default in Android). In case ofasync, exceptions are thrown when callingawait(). For finer control, useCoroutineExceptionHandlerorSupervisorJob.
import kotlinx.coroutines.* fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("Caught exception: $exception") } val job = GlobalScope.launch(handler) { // Apply handler throw IllegalStateException("Something went wrong") } job.join() // Wait for completion } - Cancellation: