Kotlin Coroutines in Android

16 Dec. 20
97 VIEWS

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.

Author

Lets Nurture
Posted by Lets Nurture

Blog A directory of wonderful things

AdMob Mediation in Android

We all know about Admob Ads very well. Here, I would like to explain what mediation features and how it will be useful for developers. Let’s take a simple one …

Laundry On-Demand services: A Successful App Service You Should Consider

With our hectic lifestyles and the need to physically distance due to the ongoing pandemic, on-demand app services are rising in popularity. One such category for this niche is on-demand …

How to Setup Twilio Package for SMS in Laravel

In your application workflow, you may need to pass important information to your users. Most services require your users to have an internet connection and unfortunately, this isn’t always the …

Firebase Dynamic Links

Deep links are a powerful and essential part of a market’s toolkit. They are connecting users to campaigns, streamlining user experience and exposing users to the right marking. Many developers …

Google in app purchase in android app

Before we start with app purchase, Let’s get a basic idea of what type of digital content we are selling to users. Google’s Billing system gives us mainly two types …

COVID lockdown has brought the digital future forward

The Novel Coronavirus epidemic that has spread its tentacles worldwide almost brought business and the economy to its knees. With no way of attending office, how are employees supposed to …

The Impact of COVID 19 on Restaurant & Hospitality Industry

Coronavirus has literally crippled the economies of different countries catastrophically and has forced many industries to put down their shutters for long. Some have closed down temporarily abiding by the …

How AI might be the most effective weapon to detect COVID-19

The COVID-19 pandemic raises many problems in the domain of Artificial Intelligence (AI). The major challenges are often raised by way of so many pertinent questions such as, can AI …

AI for Pneumonia Detection

The risk of pneumonia is enormous for many, especially in the nations where billions face energy poverty and rely on polluting forms of energy. “The WHO estimates that over 4 …

How Outsourcing & Subcontracting help companies with Disaster Management

According to recent google reports, 114 countries have reported that 1,18,000 have contracted Covid-19, the disease caused by the virus, known as SARS-CoV2. Nearly 4,300 people have died. Introduction Where …

10 most extensively used Python libraries

Python, being one of the most sought after programming languages, has a huge collection of libraries. In fact, this expansive set of libraries can be considered as one of the …

AI for Food Detection

“Four simple ways to fight diabetes/Obesity: Go for regular medical check-ups; Exercise more; Watch your diet, and Cut down on soft drinks.” – Singapore PM Lee Hsien Loong (Nvidia Research …

Django vs Laravel vs Node js

Web Frameworks are basically software packages that ease the complexity of web development. They come with many inbuilt features that are common in web development, thus minimizing the development time, …

8 Chatbot Development Frameworks: Building a Better Bot for Your Business

There has been an explosion in the use of chatbots across both business websites and messaging applications, mainly because businesses want to cater to their customers and customers have a …

Implementation of AI in Financial Planning

AI focuses on a future where machines not only do all of the manual labour, as they have done since the industrial revolution but also the work which requires Intelligence …

Importance of reliable IT outsourcing partner during a global pandemic

Though a bad reputation has developed over the years, outsourcing is on the rise and for good reason: it is a sound financial strategy for companies, of all sizes and …

Tech in Prenatal Care-Empowering rural women with a mobile first avenue for information

In the era of wireless technology, where there are more mobile phones than people, mobile applications play an important role for rural women to improve their maternal health. Technology is …

Survival of the Fittest is now the Survival of the Agilest

A joint study by Forbes magazine and Scrum Alliance has found that Agile practices are gaining a lot more popularity among SME businesses and across sizes that and include startups, …

How agile will transform the organization framework?

Innovation is no longer just about new technology, it is about new models of organization. The high tech breakthroughs that do count today are not only about speed and performance …

The time is now for Machine Learning

Have you heard your peers discussing Machine Learning (ML) yet have only a vague idea of what that implies? It is safe to say that you are burnt out on …

CONTACT US

Have an !dea or need help with your current business?

loading...
We use cookies to give you tailored experiences on our website.
Okay