picKotlin

Concurrency and Coroutines in Kotlin

The Java Virtual Machine, or JVM for short, supports multithreading. Any process you run on it is free to create a reasonable number of threads to perform multiple tasks asynchronously. However, writing code that can do so in an optimal and error-free manner can be extremely hard. Over the years, Java, other JVM languages, and a lot of third-party libraries have tried to come up with creative and elegant approaches to address this problem.

For instance, Java 5 introduced the executor framework, which allows you to decouple thread management details from your business logic. Java 8 offers parallel streams, which can easily be used with lambda expressions. RxJava brings reactive extensions to Java, allowing you to write very concise and readable asynchronous code.

Kotlin supports almost all of those approaches and offers a few of its own. In this tutorial, I’ll show you how you can use them in Android apps.

Prerequisites

To be able to follow this tutorial, you’ll need:

  • Android Studio 2.3.3 or higher
  • Kotlin plugin 1.1.51 or higher
  • a basic understanding of Java threads

If you aren’t comfortable working with lambda expressions and SAM interfaces, I suggest you also read the following tutorial before proceeding:

  • Android SDK
    Quick Tip: Write Cleaner Code With Kotlin SAM Conversions
    Ashraff Hathibelagal

You can also learn all the ins and outs of the Kotlin language in our Kotlin From Scratch series.

  • Kotlin From Scratch: Nullability, Loops, and Conditions

    Kotlin
  • Kotlin From Scratch: More Fun With Functions

    Kotlin

1. Creating Threads

Usually, instances of classes that implement the Runnable interface are used to create threads in Kotlin. Because the Runnable interface has just one method, the run() method, you can leverage Kotlin’s SAM conversion feature to create new threads with minimal boilerplate code.

Here’s how you can use the thread() function, which is a part of Kotlin’s standard library, to quickly create and start a new thread:

The above approach is appropriate only when you need to occasionally spawn a thread or two. If concurrency is an important part of your app’s business logic and you need a large number of threads, using thread pools with an executor service is a better idea. 

For example, the following code uses the newFixedThreadPool() method of the Executors class to create a thread pool containing eight reusable threads and runs a large number of background operations on it:

It might not be obvious at first glance but, in the above code, the argument to the submit() method of the executor service is actually a Runnable object.

2. Getting Results From Threads

Background tasks created using the Runnable interface cannot return any results directly. If you want to receive results from your threads, you must use the Callable interface instead, which is also a SAM interface.

When you pass a Callable object to the submit() method of an executor service, you receive a Future object. As its name suggests, the Future object will contain the result of the Callable at some point in the future, when the executor service has finished running it. To get the actual result from a Future object, all you need to do is call its get() method—but beware, your thread will block if you call it prematurely.

The following sample code shows you how to create a Callable object that returns a Future of type String, run it, and print its result:

3. Synchronizing Threads

Unlike Java, Kotlin doesn't have the synchronized keyword. Therefore, to synchronize multiple background operations, you are expected to use either the @Synchronized annotation or the synchronized() standard library inline function. The annotation can synchronize an entire method, and the function works on a block of statements.

Both the @Synchronized annotation and the synchronized() function use the concept of monitor locks. 

If you don't already know, every object on the JVM has a monitor associated with it. For now, you can think of a monitor as a special token that a thread can acquire, or lock, to gain exclusive access to the object. Once an object's monitor is locked, other threads that want to work on the object will have to wait until the monitor is released, or unlocked, again.

While the @Synchronized annotation locks the monitor of the object the associated method belongs to, the synchronized() function can lock the monitor of any object that's passed to it as an argument.

4. Understanding Coroutines

Through an experimental library, Kotlin offers an alternative approach to achieve concurrency: coroutines. Coroutines are far lighter than threads and are much easier to manage.

In mobile multithreaded applications, threads are usually used for operations such as fetching information from the Internet or querying databases. Such operations don't involve much computation, and the threads spend most of their lifetime in a blocked state, just waiting for data to come from somewhere else. As you can probably tell, that's not a very efficient way to use the CPU.

Coroutines are designed to be used instead of threads for such operations. The most important thing you need to understand about coroutines is that they are suspendable. In other words, instead of blocking, they can simply stop when necessary and seamlessly continue later. This leads to much better CPU utilization. Indeed, with well-designed coroutines, you can effortlessly run dozens of background operations.

To be able to use coroutines in your Android Studio project, make sure you add the following compile dependency in the app module's build.gradle file:

5. Creating Suspending Functions

A coroutine can be suspended only with the help of a suspending function. Therefore, most coroutines have calls to at least one such function inside them.

To create a suspending function, all you need to do is add the suspend modifier to a regular function. Here's a typical suspending function executing an HTTP GET request using the khttp library:

Note that a suspending function can be called only by a coroutine or another suspending function. If you try calling it from anywhere else, your code will fail to compile.

6. Creating Coroutines

When it comes to creating a new coroutine, Kotlin's standard library has enough coroutine builders to make you feel spoiled for choice. The simplest coroutine builder you can use is the launch() function, and like most other coroutine builders, it expects a suspending lambda, which is nothing but an anonymous suspending function. As such, this lambda is what becomes the coroutine.

The following code creates a coroutine that makes two sequential calls to the suspending function we created in the previous step:

The return value of the launch() function is a Job object, which you can use to manage the coroutine. For example, you can call its join() method to wait for the coroutine to complete. Similarly, you can call its cancel() method to immediately cancel the coroutine.

Using the launch() function is much like creating a new thread with a Runnable object, primarily because you can't return any value from it. If you want to be able to return a value from your coroutine, you must create it using the async() function instead.

The async() function returns a Deferred object, which, just like the Job object, allows you to manage the coroutine. However, it also allows you to use the await() function to wait for the result of the coroutine without blocking the current thread.

For instance, consider the following coroutines that use the fetchWebsiteContents() suspending function and return the content lengths of two different webpage addresses:

With the above code, both the coroutines will start immediately and run in parallel.

If you now want to use the returned lengths, you must call the await() method on both the Deferred objects. However, because the await() method too is a suspending function, you must make sure that you call it from another coroutine.

The following code shows you how to calculate the sum of the two lengths using a new coroutine created with the launch() function:

7. Using Coroutines in the UI Thread

Coroutines do make use of background threads internally, which is why they don't run on an Android app's UI thread by default. Consequently, if you try modifying the contents of your app's user interface from inside a coroutine, you will encounter a runtime error. Fortunately, it's trivially easy to run a coroutine on the UI thread: you just have to pass the UI object as an argument to your coroutine builder.

For example, here's how you can rewrite the last coroutine to display the sum inside a TextView widget:

The above code might seem mundane at first, but look again. It's not only able to wait for two background operations to complete without using callbacks, it's able to do so on the app's UI thread without blocking it!

Having the ability to wait on the UI thread, without making your UI feel sluggish or triggering an Application Not Responding error, often referred to as ANR, simplifies a lot of otherwise complex tasks. 

For instance, with the suspending delay() function, which is the non-blocking equivalent of the Thread.sleep() method, you can now create animations with loops. To help you get started, here's a sample coroutine that increments the x coordinate of a TextView widget every 400 ms, thus creating a marquee-like effect:

Conclusion

While developing Android apps, it is imperative that you perform long-running operations in background threads. In this tutorial, you learned several approaches you can follow to create and manage such threads in Kotlin. You also learned how to use the still experimental coroutines feature to wait inside threads without blocking them.

To learn more about coroutines, you can refer to the official documentation. And while you're here, check out some of our other posts about Kotlin and Android development!

  • Android SDK
    How to Create an Android Chat App Using Firebase
    Ashraff Hathibelagal
  • Android SDK
    Sending Data With Retrofit 2 HTTP Client for Android
    Chike Mgbemena
  • Android SDK
    Get Started With RxJava 2 for Android
    Jessica Thornsby
  • Android SDK
    Java vs. Kotlin: Should You Be Using Kotlin for Android Development?
    Jessica Thornsby

Powered by WPeMatico

Leave a Comment

Scroll to Top