Understanding of Android Coroutines: 10 Key Points Explained with Code Examples

Abi FarhanAbi Farhan
4 min read

1. Coroutines Basics

Coroutines are a lightweight concurrency design pattern that allows you to write asynchronous code in a sequential and readable manner. They are built on suspending functions, which can be paused and resumed without blocking the thread.

2. Suspending Functions

Suspending functions are functions that can be paused and resumed later, enabling asynchronous and non-blocking operations. They are defined with the suspend modifier and can only be called from within a coroutine or another suspending function.

3. Coroutine Context and Dispatchers

Coroutine Context represents the context in which a coroutine runs and includes information like scope, dispatcher, and error handling. Dispatchers define the thread or thread pool on which the coroutine runs, such as Dispatchers.Main for the main UI thread.

4. Coroutine Builders

Coroutine builders are functions that create and start coroutines. The most common builder is launch, which starts a new coroutine and doesn't return a result. Other builders like async return a Deferred represents a future result.

5. Coroutine Scopes

Coroutine scopes define the lifetime of a coroutine. It's recommended to use structured concurrency and create a custom coroutine scope tied to the lifecycle of an activity or fragment instead of relying on GlobalScope.

6. Coroutine Exception Handling

Exceptions within coroutines can be caught and handled using try-catch blocks. Use the CoroutineExceptionHandler to define a global exception handler for all coroutines within a scope.

7. Suspending Functions for Asynchronous Operations

Suspending functions are commonly used for asynchronous operations like network requests or database queries. Libraries like Retrofit or Room provide suspending versions of their API calls. Use withContext to switch to an appropriate dispatcher for blocking operations.

8. Coroutine Concurrency and Parallelism

Coroutines enable concurrent and parallel operations. Concurrent coroutines can run on the same thread, while parallel coroutines execute on different threads. Use async to perform parallel operations and await their results using await or async.awaitAll().

9. Coroutine Cancellation

Coroutines can be canceled by calling cancel() on the coroutine's Job. Cancellation is cooperative, and suspending functions should check for cancellation by calling yield() or isActive periodically. Structured concurrency with coroutineScope ensures cancellation propagates to child coroutines.

10. Coroutine Flow

Coroutine Flow is an API for handling streams of data asynchronously. It allows for processing and transforming data emitted over time. Use flow and collect functions to create and consume flows respectively.


Great! Now that we have a solid understanding of coroutines, let's delve into the practical implementation of code. In this simulation, we will showcase how to retrieve and display a list of Coffee Menu items from an API.

Let's get started!

Step 1: Import the necessary dependencies:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'

Step 2: Create a suspending function to fetch the coffee menu data from the API:

suspend fun fetchCoffeeMenu(): List<CoffeeMenu> {
    // Perform API request and return the coffee menu data
}

Step 3: Define the CoroutineScope and Dispatchers in your activity:

private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)

Step 4: Launch a coroutine to fetch the coffee menu data:

coroutineScope.launch {
    try {
        val coffeeMenuList = fetchCoffeeMenu()
        // Update UI with the coffee menu list
    } catch (e: Exception) {
        // Handle error fetching data
    }
}

Step 5: Handle exceptions using CoroutineExceptionHandler:

private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
    // Handle exceptions here
}

Launch the coroutine with the exception handler:

coroutineScope.launch(exceptionHandler) {
    // Coroutine code here
}

Step 6: Implement the fetchCoffeeMenu() function with suspending API call:

suspend fun fetchCoffeeMenu(): List<CoffeeMenu> {
    return withContext(Dispatchers.IO) {
        // Perform API request and return the coffee menu data
    }
}

Step 7: Implement the coffee menu API request:

suspend fun fetchCoffeeMenu(): List<CoffeeMenu> {
    return withContext(Dispatchers.IO) {
        val response = apiService.getCoffeeMenu()
        if (response.isSuccessful) {
            response.body() ?: emptyList()
        } else {
            throw ApiException("Failed to fetch coffee menu")
        }
    }
}

Step 8: Implement cancellation in the fetchCoffeeMenu() function:

suspend fun fetchCoffeeMenu(): List<CoffeeMenu> {
    return withContext(Dispatchers.IO) {
        val response = apiService.getCoffeeMenu()
        if (response.isSuccessful) {
            response.body() ?: emptyList()
        } else {
            throw CancellationException("Failed to fetch coffee menu")
        }
    }
}

Step 9: Use coroutineScope to ensure cancellation propagates:

coroutineScope.launch {
    try {
        val coffeeMenuList = fetchCoffeeMenu()
        // Update UI with the coffee menu list
    } catch (e: CancellationException) {
        // Handle cancellation
    } catch (e: Exception) {
        // Handle other exceptions
    }
}

Step 10: Implement Coroutine Flow for handling streams of data:

fun fetchCoffeeMenu(): Flow<List<CoffeeMenu>> = flow {
    val response = apiService.getCoffeeMenu()
    if (response.isSuccessful) {
        emit(response.body() ?: emptyList())
    } else {
        throw ApiException("Failed to fetch coffee menu")
    }
}

Collect the flow in the activity:

coroutineScope.launch {
    try {
        fetchCoffeeMenu().collect { coffeeMenuList ->
            // Update UI with the coffee menu list
        }
    } catch (e: CancellationException) {
        // Handle cancellation
    } catch (e: Exception) {
        // Handle other exceptions
    }
}
0
Subscribe to my newsletter

Read articles from Abi Farhan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Abi Farhan
Abi Farhan

Working professionally with high passion and curiosity about Entrepreneurship, Tech, and Coffee. I am willing to work hard, learn faster, and extremely fast adaptation to new tech stacks. I have more 3 years of experience as a Software Engineer in the Industry world and academic world, I believe with my experience combined with my abilities as an Entrepreneur and Software Engineer with a deep understanding of data structure and algorithms also as strong skills for problem-solving would make me a valuable asset too much technology-oriented business seeking the talent with always hungry for knowledge and always eager to learn new things. For now, I work as Software Engineer in Aleph-Labs which develop the MyXL Ultimate app also I develop my own business Gayo Coffee Engaged in B2B and B2C. I am also Bilingual Communicator (English, Indonesia) with experience public speaker and leadership. I also experiece management project using Jira and Notion, also strong skills for teamwork including deep knowledge about Version Control System like Git.