Categories: Blog

Kotlin Coroutines in Android

Multithreading is a well known programming concept which all developers might have come across. The concept of multithreading is a vast subject and includes complex mechanisms in it, but in simpler terms, it is a subject of dealing with multiple tasks parallely, or asynchronous execution in short.

All of the major programming languages support multithreading in some way. For example, Java supports creating multiple threads or runnables, whereas Swift has DispatchQueue to handle multithreading.

What is the problem of existing multithreading solutions?

Whenever we run some blocking task on a thread, for example, waiting for a network call’s response, the entire thread is blocked, even though it’s doing nothing. This is a waste of system resources.

Inter-thread communication requires callbacks to be defined. For example, in Android, if we want to do a network call, we’d create a background thread for the call and will define a callback on the main thread that will be triggered once the network call is completed. Having more such tasks lead to a phenomenon called Callback hell, meaning a large number of callbacks leading to an ugly and unreadable code.

Kotlin Coroutines to the rescue

Kotlin is gaining more and more popularity each day because of its concise nature and smart, time-saving features like extension functions, nullable data-types, data-class, and much more. Out of all these features, the one that brings revolution to the multithreading paradigm is…. None other than Coroutines!

Coroutines are the next-gen thread-like mechanism, where each coroutine can execute a task in parallel. People call it lightweight threads. Coroutines work on top of threads, but do remember that they are not threads. A single thread can run more than one coroutine and a single coroutine can be run on top of multiple threads.

Threads are managed by CPU, but coroutines are managed by Kotlin library. Coroutines are designed in such a way that they can optimize the use of threads to save CPU/memory resources easily. Whenever we perform a task on thread, the thread is allotted to that task until it is finished, so, if our task has some blocking operation like network call or waiting for user input, the thread is not released and another task has to wait until the current thread completes. To overcome such situations, coroutines can start a work on some thread, and when it comes to a blocking operation, it suspends itself and releases the thread, so that other tasks can be performed on that thread. Once the blocking operation is complete, coroutine resumes and finishes its work.

Another benefit of using Coroutines is that we can write the code in a synchronous way, but it would work asynchronously. No need for callbacks! This might sound confusing at first, but don’t worry, we’ll find it easy when we see it in action.

Concepts of Coroutines

There are few concepts that we need to know before we can start using the coroutines.

  • Coroutine builders

    Think of coroutine builders as an entry point of coroutine. Whenever we want to perform any task inside a coroutine, we have to use the coroutine builder to start the coroutine. There are 2 coroutines builder functions provided by the library, launch() and async(). Launch is generally used when you don’t need any result of the coroutine task. Async is used when we need the result of a task performed inside coroutine.

  • Coroutine Scope

    A scope defines the life of a coroutine. Scopes can be local or global in general. Local scope means a coroutine can run until your class/object is alive. Global scope means a coroutine can run during the entire lifecycle of your application

    There are some special scopes provided as part of Android KTX libraries, such a ViewModelScope, LifeCycleScope, etc to control the coroutines with Android lifecycles.

  • Suspend function

    A suspend function is a function with suspend modifier. It is used whenever you want to do a blocking or long running operation inside a coroutine. Whenever a suspend function is called from coroutine, it is suspended and doesn’t execute the next line until the suspend function has finished its execution. The magic here is that even though we pause the execution of the task when calling suspend, it doesn’t mean we are blocking the thread. As soon as we call the suspend function, which runs on a different thread, the current thread is released so that others can use it.

  • Dispatchers

    When you launch a coroutine, it runs on top of a thread. Threads are of different kinds, such as main thread, where we do all the UI operations, IO threads, where we can perform read-write to files or network calls, etc.Dispatcher can be passed as an argument of coroutine builder.

    Coroutines provide 3 different dispatchers to choose from:

    • Dispatchers.Main: Used to do work inside main thread of application
    • Dispatchers.IO: Used to work on background threads optimized for IO operations.
    • Dispatchers.Default: Used to work on background threads optimized for CPU intensive operations.

Getting started with Coroutines on Android

To start using coroutines, we need to add following dependency to our app module’s build.gradle file:

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
}

If you are working on an MVVM architecture, and if you want to take advantages of the special scopes and shortcuts methods for coroutines, make sure these dependencies are also added:

def arch_version = "2.1.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

That’s it, you just added coroutine support to your app. Now you can run coroutines anywhere in your app.

Let’s see an example of an API call using Retrofit

We want to create the demo using Github users API. Let’s set up the Retrofit Network Manager.


object NetworkManager {

    const val BASE_URL = "https://api.github.com/"

    val retrofit: Retrofit

    init {
        val okHttpLoggingInterceptor = HttpLoggingInterceptor().apply {
            setLevel(HttpLoggingInterceptor.Level.BODY)
        }

        val okHttpClient = OkHttpClient().newBuilder()
            .callTimeout(Duration.ofSeconds(60))
            .readTimeout(Duration.ofSeconds(60))
            .writeTimeout(Duration.ofSeconds(60))
            .addInterceptor(okHttpLoggingInterceptor)
            .build()

        retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)
            .build()
    }

}

Normally, when we use Retrofit, we’d define the API interface like this:

interface UsersApiService {
    @GET("users")
    fun getUsers(): Call>
}

Here, the getUsers() returns a Call object, which means you have to call enqueue on it and you have to define success and failure callbacks.

So, the repository implementation in the traditional way would be:


class UsersRepository {

    val usersLiveData = MutableLiveData>()
    val errorLiveData = MutableLiveData("")

    fun getUsers() {
        val usersApiService = NetworkManager.retrofit.create(UsersApiService::class.java)

        usersApiService.getUsers().enqueue(object : Callback> {
            override fun onResponse(call: Call>,
                                    response: Response>) {
                if (response.isSuccessful) {
                    usersLiveData.value = response.body()
                } else {
                    errorLiveData.value = response.errorBody()?.string()
                    usersLiveData.value = mutableListOf()
                }
            }

            override fun onFailure(call: Call>, t: Throwable) {
                errorLiveData.value = t.message
            }

        })
    }

}

And in ViewModel, we’d just refer to the repository’s LiveData and call getUsers whenever we want to call the API.

class UsersViewModel: ViewModel() {

    private val repository = UsersRepository()
    val usersList: MutableLiveData> = repository.usersLiveData
    val errorMessage: MutableLiveData = repository.errorLiveData

    fun getUsers() {
        repository.getUsers()
    }

}

Now let’s convert this implementation using coroutines:

Retrofit 2.6.0 or higher supports coroutines out-of-the-box. So, make sure you’re using the latest SDK version.

The first change will be in the API interface itself, instead of returning a Call object, you have to return the actual data object. And since this API call is blocking operation to be performed inside coroutines, we have to mark the function as suspend.

interface UsersApiService {
    @GET("users")
    suspend fun getUsers(): List
}

Note: A suspend function can be called from either a coroutine or from another suspend function.

Now, in the repository, we have to mark the getUsers() as the suspend function because we’re going to call API from that. Also, since we no longer return the Call object from the interface, there’s no need to define any callback!

Change the repository function like this:

suspend fun getUsers() {
        val usersApiService = NetworkManager.retrofit.create(UsersApiService::class.java) //main thread
        val users = usersApiService.getUsers() //suspend on background thread
        usersLiveData.value = users //resume on main thread
    }

Here’ when we call usersApiService.getUsers(), we suspend the coroutine, which means the next line is only executed once we get the response of the API. Retrofit internally uses the background thread dispatchers for API calls, which means, even if we call this function from the main thread, it’ll switch to the background thread for operation. Also, when the API call is completed the next line is executed on the same thread from which the coroutine was launched, here – main thread.

And inside the ViewModel, we can launch the coroutine and call the repository suspend function to call the API. Coroutine needs a scope to launch. With the help of the ViewModel KTX library, we have a special scope called viewModelScope which can be used to launch a coroutine in ViewModel.

fun getUsers() {
    viewModelScope.launch {
       repository.getUsers() //suspend function
    }
}

Congratulations! You just migrated a traditional API call to using coroutines. Isn’t it easier? There’s no need to define any callbacks and you can do the programming synchronously, with the help of suspend functions.

To find more about Coroutines on Android, refer: Kotlin coroutines on Android
To see the full demo, find it on Github: https://github.com/maulikhirani/coroutines-playground

How Let’s Nurture helps for building apps with the latest technology like Coroutines for better performance?

Let’s Nurture, a top mobile app development company in India has got the expertise in providing many solutions based applications with E-Commerce, Social Networking, Health sector, IoT & Machine learning. Our team at LetsNurture provides best solutions for business (saving time), shopping experience, teaching experience and many more. If you want to know more about this technology in your existing mobile app, get a free consultation from our experts at Let’s Nurture.

Lets Nurture

Share
Published by
Lets Nurture

Recent Posts

7 Powerful Psychological Triggers to Skyrocket Your Website Engagement

In the digital age, understanding the hidden forces driving user behavior is essential. By strategically…

7 months ago

What is haptics? How can we implement in in AR based mobile App? What are Haptics Use cases?

What is haptics?   Haptics refers to the use of touch feedback technology to simulate…

9 months ago

The Benefits of Using Virtual Reality in Business

In today's fast-paced and technologically driven world, businesses are constantly seeking innovative ways to stay…

1 year ago

A Closer Look at New Jersey’s Thriving Incubator Ecosystem

The Garden State, more popularly known as New Jersey, is not only known for its…

1 year ago

Why You Need a Mobile App for Your Business

In today's digital age, mobile apps have become an indispensable tool for businesses across all…

1 year ago

How to Optimize Your Website for Better User Experience

In today's digital era, a seamless and enjoyable user experience is crucial for the success…

1 year ago