Android -Kotlin Coroutines

Recep Yeşilkaya
Arabam Labs
Published in
7 min readAug 12, 2024

Hello everyone! Today, we’re going to dive into Kotlin Coroutines, starting from the basics and working our way up to more advanced topics. If your coffee is ready, let’s get started ☕️

What Are Kotlin Coroutines?

Kotlin coroutines are a library that simplifies asynchronous and parallel programming, making it more readable and manageable. Coroutines allow you to perform long-running tasks, such as network calls or database operations, without blocking the main thread. But what exactly are parallel programming and asynchronous programming? Let’s explore these concepts further.

Parallel and Asynchronous Programming

Parallel and asynchronous programming allows you to perform long-running operations in the background without blocking the user interface. This greatly enhances both the user experience and the performance of your application.

Parallel programming involves breaking down a task into smaller parts and executing those parts simultaneously on different threads. This can significantly reduce processing time and offers a substantial advantage in applications that require high performance.

Asynchronous programming allows long-running tasks to be executed in the background without blocking the user interface (UI). This ensures that the application remains responsive to user interactions, preventing freezing or unresponsiveness. Beyond enhancing user experience, asynchronous programming offers numerous advantages in areas such as performance and efficiency, resource management, parallelism and concurrency.

A Light Thread, often represented by coroutines, is a term used to describe threads that consume significantly fewer system resources and start up faster than traditional threads. As a result, they offer a more efficient and scalable approach to asynchronous programming.

For example, handling 1 million operations with coroutines is feasible, while achieving the same with traditional threads would be impractical due to resource limitations.

Kotlin Coroutines vs RxJava

In Android app development, both Kotlin Coroutines and RxJava are commonly used to manage asynchronous tasks. Kotlin Coroutines, with its straightforward and understandable structure, may be more suitable for beginners and rapid development processes. On the other hand, RxJava, with its extensive feature set and flexible nature, is often preferred for complex and large projects. By choosing the right tool based on your application’s requirements, you can achieve the best performance and efficiency.

Coroutine:

  • Simpler and more readable API
  • Low memory usage and high performance
  • Easy learning curve
  • Standard error handling

RxJava:

  • Complex API based on functional programming principles
  • High performance but higher memory consumption
  • Steep learning curve
  • Complex error handling

Writing Asynchronous Code with Coroutines

A suspend function is defined like a regular function but is marked with the suspend keyword. These functions can only be called from within another coroutine or a suspend function.

The Mechanism Behind suspend Functions

suspend functions rely on a mechanism that allows coroutines to be paused and resumed. This mechanism enables long-running tasks to be executed without blocking the main thread.

  1. Coroutine Dispatcher: suspend functions operate on a specific dispatcher. Dispatchers determine which thread a coroutine will run on.
  2. Continuation: When a suspend function is called, a continuation is created. This continuation remembers where the function was paused and resumes from that point when it is restarted.
  3. Suspension Point: When a suspend function, like delay, is called, the coroutine is paused, creating a suspension point. This allows the coroutine to be paused and switch to other tasks.
  4. Resumption: When a certain time or condition is met, the suspended coroutine is resumed and continues from where it left off.

To understand better, you can watch the video at this link.

Basics of Kotlin Coroutines

Coroutine Scope

A coroutine scope manages the lifecycle of one or more coroutines. It is used to start and cancel coroutines. A scope is typically associated with a specific lifecycle, such as an Android Activity or ViewModel.

A scope is used by builder functions such as launch or async to start coroutines. These functions create coroutines within the context of the scope, allowing for structured concurrency and lifecycle management.

Coroutine Scope: Application Areas and Examples

  1. viewModelScope: This scope is used to launch coroutines within a Jetpack ViewModel. It is tied to the lifecycle of the ViewModel, meaning that all coroutines within this scope are automatically canceled when the ViewModel is destroyed.

2. lifecycleScope: This scope is used to launch coroutines in Android components with a lifecycle, such as Activities and Fragments. It is tied to the component's lifecycle, meaning that coroutines are automatically canceled when the component is stopped.

Using Coroutines Based on Lifecycle States

repeatOnLifecycle simplifies performing repeated tasks during a specific lifecycle state. It ensures that coroutines are safely started and stopped according to the lifecycle state, helping to prevent memory leaks and unnecessary resource usage.

  • Lifecycle.State.CREATED: Useful for operations that need to be performed when the component is created and before interacting with the user. For example, it can be used to load initial data or perform setup tasks. This block will repeat in the CREATED state and stop when the component is DESTROYED.
  • Lifecycle.State.STARTED: Used for tasks that should be initiated when the component becomes visible on the screen. It is ideal for UI updates or interactions with visible components. This block will repeat in the STARTED state and stop when the component is STOPPED.
  • Lifecycle.State.RESUMED: Used when the component is fully interacting with the user. It is suitable for tasks that need to be performed when the user is visually interacting with the UI and the component is fully visible. This block will repeat in the RESUMED state and stop when the component is PAUSED.

3. GlobalScope: Used to launch coroutines that continue throughout the application's lifecycle. However, it should be used with caution because coroutines in this scope run until the application is closed, which can potentially lead to memory leaks.

4. CoroutineScope: A general scope used to launch coroutines with a specific context. CoroutineScope can be configured with context elements such as Job and Dispatcher, allowing for fine-grained control over coroutine management and execution.

Coroutine Context

CoroutineContext defines the environment in which coroutines are executed and consists of various elements that affect coroutine behavior. These elements include:

  • Job: Manages the lifecycle and cancellability of the coroutine.
  • Dispatcher: Determines which thread the coroutine runs on.
  • ExceptionHandler: Catches and handles exceptions within the coroutine.

The CoroutineContext interface is composed of these elements, with Dispatcher being one of the most commonly used context elements. Dispatchers determine the thread on which the coroutine runs:

Dispatchers

  • Dispatchers.Main: Runs coroutines on the main thread (UI thread). Ideal for UI updates and user interactions.
  • Dispatchers.IO: Optimized for I/O operations. Used for tasks such as network requests and file reading/writing.
  • Dispatchers.Default: Optimized for CPU-intensive operations. Suitable for heavy computations and parallel tasks.
  • Dispatchers.Unconfined: Starts coroutines on the thread they are launched from. After the first suspension, the coroutine may resume on a different thread.

Customizing Coroutine Context

  • You can customize a coroutine context by combining different context elements. For example, you can run a coroutine on a specific thread while also associating it with a Job.
  • CoroutineExceptionHandler is used to catch and handle exceptions within coroutines. This ensures that coroutines shut down safely in case of errors.

By combining these elements, you can customize and optimize the working environment (context) of coroutines to meet specific needs. This allows you to ensure coroutines run on different threads, safely handle errors, and manage the lifecycle of coroutines effectively.

Coroutine builders

Coroutine builders are used to start coroutines and there are three main types: launch, async, and runBlocking. Each of these builders has its own specific use cases and features.

launch is the most commonly used builder for starting coroutines. It starts a coroutine and does not handle the result of that coroutine. It is typically used for operations with side effects.

async starts a coroutine and returns a Deferred result. This result can later be retrieved using the await function. async is typically used to start multiple asynchronous operations in parallel.

Deferred is an interface used in the Kotlin Coroutines library that represents the result of an asynchronous operation. The await() function waits for the result of the Deferred object and returns it once the operation is complete. If the coroutine is still running, the await() function will suspend and wait for the coroutine to finish.

runBlocking is typically used for tests and main functions, as it blocks the main thread or the calling thread.

The withContext function is used to execute a block of code within a specific coroutine context.

Error Handling ve Exception Management

When using Kotlin Coroutines, error and exception handling is crucial. Various mechanisms are employed to manage errors and exceptions while working with coroutines. We will cover coroutine exception handling, SupervisorJob, and CoroutineExceptionHandler with code examples.

Basic Exception Handling

To handle exceptions within a coroutine, try-catch blocks can be used.

Using SupervisorJob

SupervisorJob creates a hierarchy within coroutines, preventing the failure of child coroutines from affecting the parent coroutine. In the example below, when the first coroutine fails, the second coroutine continues to run thanks to SupervisorJob.

Using CoroutineExceptionHandler

CoroutineExceptionHandler is used to handle exceptions that occur within coroutines in a unified manner. This handler is used when starting a coroutine and catches exceptions within the coroutine.

We’ve reached the end of the Android-Kotlin Coroutine article. I hope it has been both educational and enjoyable to read. See you in the next article! 👋

Sources:

https://chatgpt.com

--

--