1. Before you begin
In this codelab you'll learn how to use Kotlin Coroutines in an Android app—the recommended way of managing background threads that can simplify code by reducing the need for callbacks.
Coroutines are a Kotlin feature that converts async callbacks for long-running tasks, such as database or network access, into sequential code.
Here is a code snippet to give you an idea of what you'll be doing:
// Async callbacks
networkRequest { result ->
// Successful network request
databaseSave(result) { rows ->
// Result saved
}
}
The callback-based code will be converted to sequential code using coroutines:
// The same code with coroutines
val result = networkRequest()
// Successful network request
databaseSave(result)
// Result saved
You will start with an existing app, built using Architecture Components, that uses a callback style for long-running tasks.
By the end of this codelab, you'll have enough experience to use coroutines in your app to load data from the network, and you will be able to integrate coroutines into an app. You'll also be familiar with best practices for coroutines, and how to write a test against code that uses coroutines.
Prerequisites
- Familiarity with the Architecture Components
ViewModel,LiveData,Repository, andRoom. - Experience with Kotlin syntax, including extension functions and lambdas.
- A basic understanding of using threads on Android, including the main thread, background threads, and callbacks.
What you'll do
- Call code written with coroutines and obtain results.
- Use suspend functions to make async code sequential.
- Use
launchandrunBlockingto control how code executes. - Learn techniques to convert existing APIs to coroutines using
suspendCoroutine. - Use coroutines with Architecture Components.
- Learn best practices for testing coroutines.
What you'll need
- Android Studio 4.1 (the codelab may work with other versions, but some things might be missing or look different).
If you run into any issues (code bugs, grammatical errors, unclear wording, etc.) as you work through this codelab, please report the issue via the Report a mistake link in the lower left corner of the codelab.
2. Getting set up
Download the code
Click the following link to download all the code for this codelab:
... or clone the GitHub repository from the command line by using the following command:
$ git clone https://github.com/android/codelab-kotlin-coroutines.git
Frequently asked questions
3. Run the starting sample app
First, let's see what the starting sample app looks like. Follow these instructions to open the sample app in Android Studio.
- If you downloaded the
kotlin-coroutineszip file, unzip the file. - Open the
coroutines-codelabproject in Android Studio. - Select the
startapplication module. - Click the
Run button, and either choose an emulator or connect your Android device, which must be capable of running Android Lollipop (the minimum SDK supported is 21). The Kotlin Coroutines screen should appear:
This starter app uses threads to increment the count a short delay after you press the screen. It will also fetch a new title from the network and display it on screen. Give it a try now, and you should see the count and message change after a short delay. In this codelab you'll convert this application to use coroutines.
This app uses Architecture Components to separate the UI code in MainActivity from the application logic in MainViewModel. Take a moment to familiarize yourself with the structure of the project.

MainActivitydisplays the UI, registers click listeners, and can display aSnackbar. It passes events toMainViewModeland updates the screen based onLiveDatainMainViewModel.MainViewModelhandles events inonMainViewClickedand will communicate toMainActivityusingLiveData.ExecutorsdefinesBACKGROUND,which can run things on a background thread.TitleRepositoryfetches results from the network and saves them to the database.
Adding coroutines to a project
To use coroutines in Kotlin, you must include the coroutines-core library in the build.gradle (Module: app) file of your project. The codelab projects have already done this for you, so you don't need to do this to complete the codelab.
Coroutines on Android are available as a core library, and Android specific extensions:
- kotlinx-coroutines-core — Main interface for using coroutines in Kotlin
- kotlinx-coroutines-android — Support for the Android Main thread in coroutines
The starter app already includes the dependencies in build.gradle.When creating a new app project, you'll need to open build.gradle (Module: app) and add the coroutines dependencies to the project.
dependencies {
...
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x.x"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:x.x.x"
}
You can find the latest version number of the Coroutines library to substitute for "x.x.x" on the Kotlin Coroutines releases page.
4. Coroutines in Kotlin
On Android, it's essential to avoid blocking the main thread. The main thread is a single thread that handles all updates to the UI. It's also the thread that calls all click handlers and other UI callbacks. As such, it has to run smoothly to guarantee a great user experience.
For your app to display to the user without any visible pauses, the main thread has to update the screen roughly every 16ms, which is about 60 frames per second. Many common tasks take longer than this, such as parsing large JSON datasets, writing data to a database, or fetching data from the network. Therefore, calling code like this from the main thread can cause the app to pause, stutter, or even freeze. And if you block the main thread for too long, the app may even crash and present an Application Not Responding dialog.
Watch the video below for an introduction to how coroutines solve this problem for us on Android by introducing main-safety.
The callback pattern
One pattern for performing long-running tasks without blocking the main thread is callbacks. By using callbacks, you can start long-running tasks on a background thread. When the task completes, the callback is called to inform you of the result on the main thread.
Take a look at an example of the callback pattern.
// Slow request with callbacks
@UiThread
fun makeNetworkRequest() {
// The slow network request runs on another thread
slowFetch { result ->
// When the result is ready, this callback will get the result
show(result)
}
// makeNetworkRequest() exits after calling slowFetch without waiting for the result
}
Because this code is annotated with @UiThread, it must run fast enough to execute on the main thread. That means, it needs to return very quickly, so that the next screen update is not delayed. However, since slowFetch will take seconds or even minutes to complete, the main thread can't wait for the result. The show(result) callback allows slowFetch to run on a background thread and return the result when it's ready.
Using coroutines to remove callbacks
Callbacks are a great pattern, however they have a few drawbacks. Code that heavily uses callbacks can become hard to read and harder to reason about. In addition, callbacks don't allow the use of some language features, such as exceptions.
Kotlin coroutines let you convert callback-based code to sequential code. Code written sequentially is typically easier to read, and can even use language features such as exceptions.
In the end, they do the exact same thing: wait until a result is available from a long-running task and continue execution. However, in code they look very different.
The keyword suspend is Kotlin's way of marking a function, or function type, available to coroutines. When a coroutine calls a function marked suspend, instead of blocking until that function returns like a normal function call, it suspends execution until the result is ready then it resumes where it left off with the result. While it's suspended waiting for a result, it unblocks the thread that it's running on so other functions or coroutines can run.
For example in the code below, makeNetworkRequest() and slowFetch() are both suspend functions.
// Slow request with coroutines
@UiThread
suspend fun makeNetworkRequest() {
// slowFetch is another suspend function so instead of
// blocking the main thread makeNetworkRequest will `suspend` until the result is
// ready
val result = slowFetch()
// continue to execute after the result is ready
show(result)
}
// slowFetch is main-safe using coroutines
suspend fun slowFetch(): SlowResult { ... }
Just like with the callback version, makeNetworkRequest must return from the main thread right away because it's marked @UiThread. This means that usually it could not call blocking methods like slowFetch. Here's where the suspend keyword works its magic.
Compared to callback-based code, coroutine code accomplishes the same result of unblocking the current thread with less code. Due to its sequential style, it's easy to chain several long running tasks without creating multiple callbacks. For example, code that fetches a result from two network endpoints and saves it to the database can be written as a function in coroutines with no callbacks. Like so:
// Request data from network and save it to database with coroutines
// Because of the @WorkerThread, this function cannot be called on the
// main thread without causing an error.
@WorkerThread
suspend fun makeNetworkRequest() {
// slowFetch and anotherFetch are suspend functions
val slow = slowFetch()
val another = anotherFetch()
// save is a regular function and will block this thread
database.save(slow, another)
}
// slowFetch is main-safe using coroutines
suspend fun slowFetch(): SlowResult { ... }
// anotherFetch is main-safe using coroutines
suspend fun anotherFetch(): AnotherResult { ... }
You will introduce coroutines to the sample app in the next section.
5. Controlling the UI with coroutines
In this exercise you will write a coroutine to display a message after a delay. To get started, make sure you have the module start open in Android Studio.
Understanding CoroutineScope
In Kotlin, all coroutines run inside a CoroutineScope. A scope controls the lifetime of coroutines through its job. When you cancel the job of a scope, it cancels all coroutines started in that scope. On Android, you can use a scope to cancel all running coroutines when, for example, the user navigates away from an Activity or Fragment. Scopes also allow you to specify a default dispatcher. A dispatcher controls which thread runs a coroutine.
For coroutines started by the UI, it is typically correct to start them on Dispatchers.Main which is the main thread on Android. A coroutine started on Dispatchers.Main won't block the main thread while suspended. Since a ViewModel coroutine almost always updates the UI on the main thread, starting coroutines on the main thread saves you extra thread switches. A coroutine started on the Main thread can switch dispatchers any time after it's started. For example, it can use another dispatcher to parse a large JSON result off the main thread.
Using viewModelScope
The AndroidX lifecycle-viewmodel-ktx library adds a CoroutineScope to ViewModels that's configured to start UI-related coroutines. To use this library, you must include it in the build.gradle (Module: start) file of your project. That step is already done in the codelab projects.
dependencies {
...
// replace x.x.x with latest version
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:x.x.x"
}
The library adds a viewModelScope as an extension function of the ViewModel class. This scope is bound to Dispatchers.Main and will automatically be cancelled when the ViewModel is cleared.
Switch from threads to coroutines
In MainViewModel.kt find the next TODO along with this code:
MainViewModel.kt
/**
* Wait one second then update the tap count.
*/
private fun updateTaps() {
// TODO: Convert updateTaps to use coroutines
tapCount++
BACKGROUND.submit {
Thread.sleep(1_000)
_taps.postValue("$tapCount taps")
}
}
This code uses the BACKGROUND ExecutorService (defined in util/Executor.kt) to run in a background thread. Since sleep blocks the current thread it would freeze the UI if it were called on the main thread. One second after the user clicks the main view, it requests a snackbar.
You can see that happen by removing the BACKGROUND from the code and running it again. The loading spinner won't display and everything will "jump" to the final state one second later.
MainViewModel.kt
/**
* Wait one second then update the tap count.
*/
private fun updateTaps() {
// TODO: Convert updateTaps to use coroutines
tapCount++
Thread.sleep(1_000)
_taps.postValue("$tapCount taps")
}
Replace updateTaps with this coroutine based code that does the same thing. You will have to import launch and delay.
MainViewModel.kt
/**
* Wait one second then display a snackbar.
*/
fun updateTaps() {
// launch a coroutine in viewModelScope
viewModelScope.launch {
tapCount++
// suspend this coroutine for one second
delay(1_000)
// resume in the main dispatcher
// _snackbar.value can be called directly from main thread
_taps.postValue("$tapCount taps")
}
}
This code does the same thing, waiting one second before showing a snackbar. However, there are some important differences:
viewModelScope.launchwill start a coroutine in theviewModelScope. This means when the job that we passed toviewModelScopegets canceled, all coroutines in this job/scope will be cancelled. If the user left the Activity beforedelayreturned, this coroutine will automatically be cancelled whenonClearedis called upon destruction of the ViewModel.- Since
viewModelScopehas a default dispatcher ofDispatchers.Main, this coroutine will be launched in the main thread. We'll see later how to use different threads. - The function
delayis asuspendfunction. This is shown in Android Studio by the
icon in the left gutter. Even though this coroutine runs on the main thread, delaywon't block the thread for one second. Instead, the dispatcher will schedule the coroutine to resume in one second at the next statement.
Go ahead and run it. When you click on the main view you should see a snackbar one second later.
In the next section we'll consider how to test this function.
6. Testing coroutines through behavior
In this exercise you'll write a test for the code you just wrote. This exercise shows you how to test coroutines running on Dispatchers.Main using the kotlinx-coroutines-test library. Later in this codelab you'll implement a test that interacts with coroutines directly.
Review the existing code
Open MainViewModelTest.kt in the test folder.
MainViewModelTest.kt
class MainViewModelTest {
@get:Rule
val coroutineScope = MainCoroutineScopeRule()
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
lateinit var subject: MainViewModel
@Before
fun setup() {
subject = MainViewModel(
TitleRepository(
MainNetworkFake("OK"),
TitleDaoFake("initial")
))
}
}
A rule is a way to run code before and after the execution of a test in JUnit. Two rules are used to allow us to test MainViewModel in an off-device test:
InstantTaskExecutorRuleis a JUnit rule that configuresLiveDatato execute each task synchronouslyMainCoroutineScopeRuleis a custom rule in this codebase that configuresDispatchers.Mainto use aTestCoroutineDispatcherfromkotlinx-coroutines-test. This allows tests to advance a virtual-clock for testing, and allows code to useDispatchers.Mainin unit tests.
In the setup method, a new instance of MainViewModel is created using testing fakes – these are fake implementations of the network and database provided in the starter code to help write tests without using the real network or database.
For this test, the fakes are only needed to satisfy the dependencies of MainViewModel. Later in this code lab you'll update the fakes to support coroutines.
Write a test that controls coroutines
Add a new test that ensures that taps are updated one second after the main view is clicked:
MainViewModelTest.kt
@Test
fun whenMainClicked_updatesTaps() {
subject.onMainViewClicked()
Truth.assertThat(subject.taps.getValueForTest()).isEqualTo("0 taps")
coroutineScope.advanceTimeBy(1000)
Truth.assertThat(subject.taps.getValueForTest
