Dec 31, 2023

What are co-routines in Kotlin ? Kotlin for Android Part -2

In this part we will learn about Kotlin's co-routines. It is an important concurrency design pattern that is extremely useful when designing asynchronous programs.

What are co-routines in Kotlin ? Kotlin for Android Part -2

Kotlin 1.3 introduced the concept of co-routes for asynchronous behaviour. In the Java world, threads have been pretty common however slightly hard to follow model. Promises solved this problem to some extent but continued to be painful to write and read code. Kotlin however borrowed from other languages some good patterns and created this feature of coroutines. In this part we will discuss this in greater depth.

Why co-routines

In Android, there is a concept of UI thread. A thread that is responsible for building UI of your App and responding to your touches and other interactions. The UI thread should be used as little as possible. If there is any long running operation that blocks UI thread there is a danger of your app becoming unresponsive and hence could lead to ANR (Application Not Responding) errors.

In Android, hence it is recommended and enforced that IO operations be done on a separate thread distinct from UI thread. This leads to the situation where you have no option but to use threads of some sort.

Kotlin's co-routines provide you a very concise syntax to create operations that run on a separate thread with separate context. Coroutines are very lightweight, have better memory management and have inbuilt cancellation support which solves a lot of common problems Android developers faced with Java's support for threads.

Using co-routines

class LoginViewModel(
    private val loginRepository: LoginRepository
): ViewModel() {

    fun login(username: String, token: String) {
        // Create a new coroutine to move the execution off the UI thread
        viewModelScope.launch(Dispatchers.IO) {
            val jsonBody = "{ username: \"$username\", token: \"$token\"}"
            loginRepository.makeLoginRequest(jsonBody)
        }
    }
}

Above simple code is take from official website of android documentation. The most common requirement for asynchronous operations is network request. Network requests are IO operations that can take a very long time to execute and it bad user experience to freeze the UI during that time.

In above code, you see a special variable viewModelScope. Each coroutine is executed in a "scope". ViewModel has its own scope but you can define your own scopes as well. Each coroutine MUST run in a scope. A scope controls related coroutines. A scope manages lifecycle of all the coroutines that are running in it. For example when the scope itself is cancelled it can cancel all the coroutines as well.

The next important bit is the method launch. This takes one argument which is the dispatcher to be used to run the coroutine. For example in above code we use IO dispatcher which tells the scope that this is a coroutine meant to execute IO operations. Other dispatches are Default, Main and Unconfined. Which are to be used for CPU intensive operations, UI related interactions and for tasks that require a dedicated thread respectively.

Suspend functions

Those who have worked with Dart or NodeJs would know the "async" modifier of methods. When you have a function defined as "async", wherever you invoke it, you have to await the result. Do not worry if you don't know what I am talking about.

Imagine you are writing a Kotlin method for login. As the author you know that this is a method that is doing IO heavy operation. But other developers who are going to invoke your method might not know this. To fix this you can simply add a modifier to your method called "suspend" which forces other developers to always make sure this method is run in a coroutine. Also as the author of the method can actually decide what dispatcher to use.

class LoginRepository(...) {
    ...
    suspend fun makeLoginRequest(
        jsonBody: String
    ): Result<LoginResponse> {

        // Move the execution of the coroutine to the I/O dispatcher
        return withContext(Dispatchers.IO) {
            // Blocking network request code
        }
    }
}

Handling Exceptions

The hardest part of async code is handling exceptions. Coroutines recommend you use a Exception to catch any possible exception and handle it appropriately instead of bubbling it out.

class LoginViewModel(
    private val loginRepository: LoginRepository
): ViewModel() {

    fun makeLoginRequest(username: String, token: String) {
        viewModelScope.launch {
            val jsonBody = "{ username: \"$username\", token: \"$token\"}"
            val result = try {
                loginRepository.makeLoginRequest(jsonBody)
            } catch(e: Exception) {
                Result.Error(Exception("Network request failed"))
            }
            when (result) {
                is Result.Success<LoginResponse> -> // Happy path
                else -> // Show error in UI
            }
        }
    }
}

Sharing mutable state in co-routines

How do different coroutines share state ? It is not possible to simply share variables in coroutines because the changes to that data structure are not always atomic. You must use thread safe data structures when dealing with coroutines.

val counterContext = newSingleThreadContext("CounterContext") var counter = 0
fun main() = runBlocking { 
withContext(Dispatchers.Default) { 
 massiveRun { 
// confine each increment to a single-threaded context 
  withContext(counterContext) { counter++ }
 } 
} 
println("Counter = $counter") }

Conclusion

Coroutines are must know concept for Android developers using Kotlin for app development.

Continue Reading
Learn about other front end technologies

Learn about other front end technologies

We are partnering with Frontendeng.dev to cross promote the content around front end engineering.

Published Dec 12, 2023

Android paired with Windows Laptops

Android paired with Windows Laptops

The new trend is to build Android and Windows Laptop into one hardware.

Published Jan 11, 2024

Backends for your Android App

Backends for your Android App

In this article, we will explore the various options available for app developers who are primarily focused on building an app backend.

Published Jan 1, 2024

Google Flutter vs Android Jetpack Compose : A detailed comparison

Google Flutter vs Android Jetpack Compose : A detailed comparison

Flutter code looks remarkably similar to Android Jetpack Compose. However which one of them is better ? We find out in this article.

Published Jan 11, 2024

Minimal starting template for Android Compose

Minimal starting template for Android Compose

Find a simple minimal code to open an app with top and bottom bar. Find full code on https://github.com/Wiseland-Inc/dev.androidauthority.app

Published Dec 11, 2023

Spring Boot for App Backends

Spring Boot for App Backends

Simple spring boot template to deploy on Google Cloud and to be used as your app backend.

Published Jan 6, 2024

Learning Kotlin for Android development - Part 1

Learning Kotlin for Android development - Part 1

A guide focused on teaching you basics of Kotlin just for Android development.

Published Dec 7, 2023

Making android development less painful

Making android development less painful

Android development has always been challenging and frustrating right from start. However things are looking better now.

Published Dec 4, 2023

Understanding Layouts in Android Compose

Understanding Layouts in Android Compose

A very quick tutorial on compose and layouts.

Published Dec 10, 2023

What are adaptive android apps ?

What are adaptive android apps ?

Adaptive apps are app equivalent of responsive web apps. Apps that resize to give good user experience across several devices.

Published Dec 9, 2023