Kotlin Coroutines Tutorial for Android: Getting Started
In this Kotlin coroutines tutorial, you’ll learn how to write asynchronous code just as naturally as your normal, synchronous code. By Luka Kordić.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Kotlin Coroutines Tutorial for Android: Getting Started
25 mins
- Why Use Kotlin Coroutines?
- Getting Started
- Adding Kotlin Coroutines Support
- Introduction to Kotlin Coroutines
- Suspending vs. Blocking
- Creating Your First Coroutine
- Coroutine Builders
- Coroutine Scope
- GlobalScope
- Downloading Images in Parallel With async
- Returning a Single Value From a Coroutine
- Coroutine Context
- Coroutine Dispatchers
- Improving Snowy’s Performance
- Canceling a Coroutine
- Coroutine Job
- Error Handling in Coroutines
- Coroutine Exception Handler
- Try/Catch
- Where to Go From Here?
Creating Your First Coroutine
Open TutorialFragment.kt, and navigate to downloadSingleImage
. Replace // TODO: Not implemented
with the following code:
lifecycleScope.launch {
val originalBitmap = getOriginalBitmap(tutorial)
val snowFilterBitmap = loadSnowFilter(originalBitmap)
loadImage(snowFilterBitmap)
}
In this method, you combine two concepts from the Kotlin Coroutines API to launch a new coroutine and execute some code inside it. lifecycleScope
is a coroutine scope and launch
is a coroutine builder. You simply call launch
on an instance of CoroutineScope
and pass a block of code that you want to execute. This is all it takes to create a new coroutine and execute some code in it. Simple, right? :]
A great thing when using Kotlin Coroutines is that the code you write is sequential and looks pretty much like regular blocking code. Once you call getOriginalBitmap
, the coroutine will suspend until the bitmap is ready. Meanwhile, the thread this code runs in is free to do other work. When the bitmap becomes available, the coroutine will resume and will execute loadSnowFilter
and loadImage
after that.
Before you proceed to the next section, where you’ll learn more about coroutine builders, examine getOriginalBitmap
:
//1
private suspend fun getOriginalBitmap(tutorial: Tutorial): Bitmap =
//2
withContext(Dispatchers.IO) {
//3
URL(tutorial.imageUrl).openStream().use {
return@withContext BitmapFactory.decodeStream(it)
}
}
This method is invoked from inside the coroutine you’ve just created. Here’s a breakdown of what it does:
- Notice that the method is marked with the
suspend
modifier. This means that the method has the ability to suspend the execution of a coroutine it’s currently running in. This is really important in this case because the method is doing a heavy operation that could potentially block the main thread. -
withContext(Dispatchers.IO)
makes sure that you switch the heavy work to a worker thread. You’ll learn aboutDispatchers
andwithContext
in the following sections. For now, just remember that this is used to offload the work to another thread. - You open a connection to the specified URL, which returns an instance of
InputStream
for reading from that connection. This piece of code downloads the image.
Build and run the app to see what you’ve done so far:
You’ll see a Kotlin image with the snow filter applied in the first tab. If you try to navigate to other tabs, you’ll only see a placeholder image. That’s OK for now — you’ll fix it later. Right now, it’s time to learn a bit more about coroutine builders.
Coroutine Builders
You can create new coroutines in a couple of different ways. The API has several constructs at your disposal, each intended for a different purpose. Since this is an introduction to Kotlin Coroutines, you’ll learn only about the essential ones:
-
launch
: The most often used coroutine builder. It creates a new coroutine and returns a handle to the newly created coroutine as a Job object. You’ll learn how to use jobs for canceling a coroutine in a later section. You use this builder when you don’t need to return a value from the coroutine. -
async
: Used when you want to return a value from a coroutine in a postponed way. It launches a new coroutine and returns aDeferred
object, which contains the operation’s result. To get a value from theDeferred
object, you need to callawait
on it. You can also use this builder to do things in parallel. You’ll see this later in the tutorial when you download two images.
Notice that you invoke the launch builder on lifecycleScope
. Both async
and launch
are defined as extension functions on CoroutineScope
. Check the following section for more details about the coroutine scope.
Coroutine Scope
A coroutine scope determines how long a coroutine lives. It does that by providing a parent context to coroutines created inside it. That’s why every coroutine builder is defined as an extension function on the scope.
In Android apps, you have two predefined scopes ready to be used: lifecycleScope
and viewModelScope
. In downloadSingleImage
, you used lifecycleScope
, which is tied to the lifecycle of the current lifecycle owner. This means that all coroutines created in lifecycleScope
will be canceled once this fragment is destroyed. This is a great concept because you don’t need to keep track of the coroutines manually. Everything’s done automatically for you. As a general rule, you should use the predefined scopes to create your coroutines.
GlobalScope
Coroutines API also contains GlobalScope
. This scope stays active as long as an app is alive. It’s considered a delicate API because it can be easily misused and cause memory leaks. You can check the official docs for more information about it. You won’t use it in this tutorial.
Downloading Images in Parallel With async
For the Kotlin tutorial, you only downloaded one image. Other tutorials in the app have two image URLs. In this section, you’ll use the async
coroutine builder to download both images in parallel.
Open TutorialFragment.kt, and navigate to downloadTwoImages
. Replace // TODO: Not implemented
with this code:
// 1
lifecycleScope.launch {
// 2
val deferredOne = lifecycleScope.async {
getOriginalBitmap(tutorial)
}
// 3
val deferredTwo = lifecycleScope.async {
val originalBitmap = getOriginalBitmap(tutorial)
loadSnowFilter(originalBitmap)
}
// 4
loadTwoImages(deferredOne.await(), deferredTwo.await())
}
Here’s what the code does:
- Launches a new coroutine in
lifecyleScope
. This is the same as in the previous example. - Creates a new coroutine in
lifecycleScope
, returns an implementation ofDeferred
and stores it indeferredOne
‘s value. This coroutine will download and show the original image. - Creates a new coroutine in
lifecycleScope
, returns an implementation ofDeferred
and stores it indeferredTwo
‘s value. This coroutine will download and show the original image with the snow filter applied. - Calls
await
on bothdeferredOne
anddeferredTwo
. This suspends the coroutine until both of the values are fully computed.
When you create a new coroutine by using async
, the system starts its execution immediately, but it also returns a future value wrapped in a Deferred
object.
To get the value, you need to call await
on the deferred instance. If the value isn’t ready yet, the coroutine will suspend. If it’s ready, you’ll get it back immediately.
This is a very powerful concept, and it can significantly speed up your code when you need to perform several long-running operations. But what if you have only one piece of work and you need to return its result? You’ll find that answer in the next section.
Build and run the app and navigate to the Android tab to see the two images: