Reader small image

You're reading from  Android Studio 4.2 Development Essentials - Kotlin Edition

Product typeBook
Published inAug 2021
Reading LevelIntermediate
PublisherPackt
ISBN-139781803231549
Edition1st Edition
Languages
Right arrow
Author (1)
Neil Smyth
Neil Smyth
author image
Neil Smyth

Neil Smyth has over 25 years of experience in the IT industry, including roles in software development and enterprise-level UNIX and Linux system administration. In addition to a bachelor's degree in information technology, he also holds A+, Security+, Network+, Project+, and Microsoft Certified Professional certifications and is a CIW Database Design Specialist. Neil is the co-founder and CEO of Payload Media, Inc. (a technical content publishing company), and the author of the Essentials range of programming and system administration books.
Read more about Neil Smyth

Right arrow

63. An Introduction to Kotlin Coroutines

The previous chapter introduced the concepts of threading on Android and explained how the user interface of an app runs on the main thread. To avoid degrading or interrupting user interface responsiveness, it is important that time consuming tasks not block the execution of the main thread. One option, as outlined in the previous chapter, is to run any such tasks on a background thread, thereby leaving the main thread to continue managing the user interface. This can be achieved either directly using thread handlers or by making use of the AsyncTask class.

Although AsyncTask and thread handlers provide a way to perform tasks on separate threads, it can be time consuming to implement, and confusing to read and maintain the associated code in an app project. This approach is also not the most efficient solution when large numbers of threads are required by an app.

Fortunately, Kotlin provides a lightweight alternative in the form of Coroutines...

63.1 What are Coroutines?

Coroutines are blocks of code that execute asynchronously without blocking the thread from which they are launched. Coroutines can be implemented without having to worry about building complex AsyncTask implementations or directly managing multiple threads. Because of the way they are implemented, coroutines are much more efficient and less resource intensive than using traditional multi-threading options. Coroutines also make for code that is much easier to write, understand and maintain since it allows code to be written sequentially without having to write callbacks to handle thread related events and results.

Although a relatively recent addition to Kotlin, there is nothing new or innovative about coroutines. Coroutines in one form or another have existed in programming languages since the 1960s and are based on a model known as Communicating Sequential Processes (CSP). In fact, Kotlin still uses multi-threading behind the scenes, though it does so...

63.2 Threads vs Coroutines

A problem with threads is that they are a finite resource expensive in terms of CPU capabilities and system overhead. In the background, a lot of work is involved in creating, scheduling and destroying a thread. Although modern CPUs are able to run large numbers of threads, the actual number of threads that can be run in parallel at any one time is limited by the number of CPU cores (though newer CPUs have 8 cores, most Android devices contain CPUs with 4 cores). When more threads are required than there CPU cores, the system has to perform thread scheduling to decide how execution of these threads is to be shared between the available cores.

To avoid these overheads, instead of starting a new thread for each coroutine and then destroying it when the coroutine exits, Kotlin maintains a pool of active threads and manages how coroutines are assigned to those threads. When an active coroutine is suspended it is saved by the Kotlin runtime and another coroutine...

63.3 Coroutine Scope

All coroutines must run within a specific scope which allows them to be managed as groups instead of as individual coroutines. This is particularly important when canceling and cleaning up coroutines, for example when a Fragment or Activity is destroyed, and ensuring that coroutines do not “leak” (in other words continue running in the background when they are no longer needed by the app). By assigning coroutines to a scope they can, for example, all be canceled in bulk when they are no longer needed.

Kotlin provides some built-in scopes as well as the option to create custom scopes using the CoroutineScope class. The built-in scopes can be summarized as follows:

GlobalScope – GlobalScope is used to launch top-level coroutines which are tied to the entire lifecycle of the application. Since this has the potential for coroutines in this scope to continue running when not needed (for example when a Activity exits) use of this scope...

63.4 Suspend Functions

A suspend function is a special type of Kotlin function that contains the code of a coroutine. It is declared using the Kotlin suspend keyword which indicates to Kotlin that the function can be paused and resumed later, allowing long-running computations to execute without blocking the main thread.

The following is an example suspend function:

suspend fun mySlowTask() {

    // Perform long running task here

}

63.5 Coroutine Dispatchers

Kotlin maintains threads for different types of asynchronous activity and, when launching a coroutine, it will be necessary to select the appropriate dispatcher from the following options:

Dispatchers.Main – Runs the coroutine on the main thread and suitable for coroutines that need to make changes to the UI and as a general purpose option for performing lightweight tasks.

Dispatchers.IO – Recommended for coroutines that perform network, disk or database operations.

Dispatchers.Default – Intended for CPU intensive tasks such as sorting data or performing complex calculations.

The dispatcher is responsible for assigning coroutines to appropriate threads and suspending and resuming the coroutine during its lifecycle. In addition to the predefined dispatchers, it is also possible to create dispatchers for your own custom thread pools.

63.6 Coroutine Builders

The coroutine builders bring together all of the components covered so far and actually launch the coroutines so that they start executing. For this purpose, Kotlin provides the following six builders:

launch – Starts a coroutine without blocking the current thread and does not return a result to the caller. Use this builder when calling a suspend function from within a traditional function, and when the results of the coroutine do not need to be handled (sometimes referred to as “fire and forget” coroutines).

async – Starts a coroutine and allows the caller to wait for a result using the await() function without blocking the current thread. Use async when you have multiple coroutines that need to run in parallel. The async builder can only be used from within another suspend function.

withContext – Allows a coroutine to be launched in a different context from that used by the parent coroutine...

63.7 Jobs

Each call to a coroutine builder such as launch or async returns a Job instance which can, in turn, be used to track and manage the lifecycle of the corresponding coroutine. Subsequent builder calls from within the coroutine create new Job instances which will become children of the immediate parent Job forming a parent-child relationship tree where canceling a parent Job will recursively cancel all its children. Canceling a child does not, however, cancel the parent, though an uncaught exception within a child created using the launch builder may result in the cancellation of the parent (this is not the case for children created using the async builder which encapsulates the exception in the result returned to the parent).

The status of a coroutine can be identified by accessing the isActive, isCompleted and isCancelled properties of the associated Job object. In addition to these properties, a number of methods are also available on a Job instance. A Job and all of its...

63.8 Coroutines – Suspending and Resuming

To gain a better understanding of coroutine suspension, it helps to see some examples of coroutines in action. To start with, let’s assume a simple Android app containing a button which, when clicked, calls a function named startTask(). It is the responsibility of this function to call a suspend function named performSlowTask() using the Main coroutine dispatcher. The code for this might read as follows:

private val myCoroutineScope = CoroutineScope(Dispatchers.Main)

fun startTask(view: View) {

    myCoroutineScope.launch(Dispatchers.Main) {

        performSlowTask()

    }

}

In the above code, a custom scope is declared and referenced in the call to the launch builder which, in turn, calls the performSlowTask() suspend function. Note that since startTask() is not a suspend function, the coroutine must be started using the launch...

63.9 Returning Results from a Coroutine

The above example ran a suspend function as a coroutine but did not demonstrate how to return results. Suppose, however, that the performSlowTask() function is required to return a string value which is to be displayed to the user via a TextView object.

To do this, we need to rewrite the suspend function to return a Deferred object. A Deferred object is essentially a commitment to provide a value at some point in the future. By calling the await() function on the Deferred object, the Kotlin runtime will deliver the value when it is returned by the coroutine. The code in our startTask() function might, therefore, be rewritten as follows:

fun startTask(view: View) {

    coroutineScope.launch(Dispatchers.Main) {

        statusText.text = performSlowTask().await()

    }

}

The problem now is that we are having to use the launch builder to start the...

63.10 Using withContext

As we have seen, coroutines are launched within a specified scope and using a specific dispatcher. By default, any child coroutines will inherit the same dispatcher as that used by the parent. Consider the following code designed to call multiple functions from within a suspend function:

fun startTask(view: View) {

    coroutineScope.launch(Dispatchers.Main) {

        performTasks()

    }

}

 

suspend fun performTasks() {

    performTask1()

    performTask2()

    performTask3()

}

 

suspend fun performTask1() {

    Log.i(TAG, "Task 1 ${Thread.currentThread().name}")

}

 

suspend fun performTask2() {

    Log.i(TAG, "Task 2 ${Thread.currentThread().name}")

}

 

suspend fun performTask3...

63.11 Coroutine Channel Communication

Channels provide a simple way to implement communication between coroutines including streams of data. In the simplest form this involves the creation of a Channel instance and calling the send() method to send the data. Once sent, transmitted data can be received in another coroutine via a call to the receive() method of the same Channel instance.

The following code, for example, passes six integers from one coroutine to another:

import kotlinx.coroutines.channels.*

.

.

val channel = Channel<Int>()

 

suspend fun channelDemo() {

    coroutineScope.launch(Dispatchers.Main) { performTask1() }

    coroutineScope.launch(Dispatchers.Main) { performTask2() }

}

 

suspend fun performTask1() {

    (1..6).forEach {

        channel.send(it)

    }

}

suspend fun performTask2...

63.12 Summary

Kotlin coroutines provide a simpler and more efficient approach to performing asynchronous tasks than that offered by traditional multi-threading. Coroutines allow asynchronous tasks to be implemented in a structured way without the need to implement the callbacks associated with typical thread-based tasks. This chapter has introduced the basic concepts of coroutines including jobs, scope, builders, suspend functions, structured concurrency and channel-based communication.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Android Studio 4.2 Development Essentials - Kotlin Edition
Published in: Aug 2021Publisher: PacktISBN-13: 9781803231549
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at £13.99/month. Cancel anytime

Author (1)

author image
Neil Smyth

Neil Smyth has over 25 years of experience in the IT industry, including roles in software development and enterprise-level UNIX and Linux system administration. In addition to a bachelor's degree in information technology, he also holds A+, Security+, Network+, Project+, and Microsoft Certified Professional certifications and is a CIW Database Design Specialist. Neil is the co-founder and CEO of Payload Media, Inc. (a technical content publishing company), and the author of the Essentials range of programming and system administration books.
Read more about Neil Smyth