The new way to do it – coroutines and flows
In this section, you will learn about the recommended approach for Android asynchronous programming: using coroutines and flows. Coroutines is a Kotlin library you can use in Android to perform asynchronous tasks. Coroutines is a library for managing background tasks that return a single value. Flows are built on top of coroutines that can return multiple values.
Kotlin coroutines
Coroutines is a Kotlin library for managing background tasks, such as making network calls and accessing files or databases, or performing long-running background tasks. Using Kotlin coroutines is Google’s official recommendation for asynchronous programming on Android. Their Android Jetpack libraries, such as Lifecycle, WorkManager, and Room-KTX, now include support for coroutines. Other Android libraries, such as Retrofit, Ktor, and Coil, provide first-class support for Kotlin coroutines.
With Kotlin coroutines, you can write your code in a sequential way. A long-running task can be made into a suspend
function. A suspend
function is a function that can perform its task by suspending the thread without blocking it, so the thread can still run other tasks. When the suspending function is done, the current thread will resume execution. This makes the code easier to read, debug, and test. Coroutines follow a principle of structured concurrency.
You can add coroutines to your Android project by adding the following lines to your app/build.gradle
file dependencies:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-
core:1.6.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-
android:1.6.0"
kotlinx-coroutines-core
is the main library for Kotlin coroutines, while kotlinx-coroutines-android
adds support for the main Android thread (Dispatchers.Main
).
To mark a function as a suspending function, you can add the suspend
keyword to it; for example, here we have a function that calls the fetchText()
function, which retrieves text from an endpoint and then displays it in the UI thread:
fun fetchText(): String {
...
}
You can make the fetchText()
function a suspending function by prefixing the suspend
keyword, as follows:
suspend fun fetchText(): String { ... }
Then, you can create a coroutine that will call the fetchText()
suspending function and display the list, as follows:
lifecycleScope.launch(Dispatchers.IO) {
val fetchedText = fetchText()
withContext(Dispatchers.Main) {
displayText(fetchedText)
}
}
lifecycleScope
is the scope with which the coroutine will run. launch
creates a coroutine to run in Dispatchers.IO
, which is a thread for I/O or network operations.
The fetchText()
function will suspend the coroutine before it starts the network request. While the coroutine is suspended, the main thread can do other work.
After getting the text, it will resume the coroutine. withContext(Dispatchers.Main)
will switch the coroutine context to the main thread, where the displayText(text)
function will be executed (Dispatchers.Main
).
In Android Studio, the Editor window identifies the suspend
function calls in your code with a gutter icon next to the line number. As shown in lines 13 and 15 in Figure 1.6, the fetchText()
and withContext()
lines have the suspend
function call gutter icon:
Figure 1.6 – Android Studio suspend function call gutter icon
You can learn more about Kotlin coroutines in Chapter 2, Understanding Kotlin Coroutines.
In the next section, you will learn about Kotlin Flows, built on top of coroutines, which can return multiple sequences of values.
Kotlin Flows
Flow is a new Kotlin asynchronous stream library that is built on top of Kotlin coroutines. A flow can emit multiple values instead of a single value and over a period of time. Kotlin Flow is ideal to use when you need to return multiple values asynchronously, such as automatic updates from your data source.
Flow is now used in Jetpack libraries such as Room-KTX and Android developers are already using Flow in their applications.
To use Kotlin Flows in your Android project, you have to add coroutines. An easy way to create a flow of objects is to use the flow{}
builder. With the flow{}
builder function, you can add values to the stream by calling emit.
Let’s say in your Android app you have a getTextFromNetwork
function that fetches text from a network endpoint and returns it as a String
object:
fun getTextFromNetwork(): String { ... }
If we want to create a flow of each word of the text, we can do it with the following code:
private fun getWords(): Flow<String> = flow {
getTextFromNetwork().split(" ").forEach {
delay(1_000)
emit(it)
}
}
Flow does not run or emit values until the flow is collected with any terminal operators, such as collect
, launchIn
, or single
. You can use the collect()
function to start the flow and process each value, as follows:
private suspend fun displayWords() {
getWords().collect {
Log.d("flow", it)
}
}
A visual representation of this flow is shown in the following figure:
Figure 1.7 – Kotlin Flow visual representation
As you can see in Figure 1.7, as soon as the getWords()
flow emits a string, the displayWords
function collects the string and displays it immediately on the logs.
You will learn more about Kotlin Flows in Chapter 5, Using Kotlin Flows.
In this section, you learned about Kotlin coroutines and flows, the recommended way to carry out asynchronous programming in Android. Coroutines is a Kotlin library for managing long-running tasks in the background. Flow is a new Kotlin asynchronous stream library, built on top of coroutines, that can emit multiple values over a period of time.