Leverage Coroutines in Android with Concurrency Essentials
Creating a performant network enabled app is easier than ever in modern Android development. This article covers some of the fundamental aspects of using coroutines while also introducing a new Kodeco course, “Concurrency & Networking in Android”. By Brian Moakley.
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
Leverage Coroutines in Android with Concurrency Essentials
15 mins
launch
The launch builder is the one you’ll use most often. It starts a new coroutine and returns a Job object. The Job is a handle to the coroutine, that you can use to check the coroutine status: whether it’s still running, has completed, or failed. You can also cancel the coroutine using the Job’s cancel
method. The launch call doesn’t block the caller thread.
The launch builder requires a CoroutineScope, which contains the context in which the coroutine runs. This context holds a set of properties accessible to the coroutine at runtime, and includes the dispatcher and the exception handler, among others.
One major feature of a CoroutineScope is that it can cancel all the coroutines that have started in it. The Kotlin Coroutines library provides extension functions for each Android entity with a lifecycle. For example, the Activity, Fragment, ViewModel and the composable functions all have their own scopes. If the given entity ends its lifecycle scope, it cancels all the coroutines started in it.
async
The async builder is similar to the launch builder—it too requires a CoroutineScope. The major difference is that the async builder returns a Deferred object, which is a mechanism that provides the ability to return a value.
The async builder is useful when you need to use the result of the coroutine or even multiple coroutines. With async, there’s no need to use any additional storage mechanism outside the coroutine to deal with the results. The async builder also differs from the launch builder in the way it handles exceptions, which is a topic for future discussion.
Cancelling Coroutines
To cancel coroutines, you can call the cancel
method on its Job. You can also cancel the entire scope – it’ll call the cancel
method on all its jobs. But, often, you won’t need to do that manually.
If you’re using the CoroutineScope associated with an Android entity, the coroutines library will handle the cancellations related to your lifecycle. For example, the scope obtained from the rememberCoroutineScope()
function in a composable will be cancelled when the composable is removed from the screen.
Calling the cancel method doesn’t cause the immediate interruption of the coroutine. That call only changes the coroutine status to cancel. The coroutine checks the status at every suspension point—the points where the coroutine can be suspended. If it turns out that the status is canceled, a CancellationException is thrown.
The coroutine can perform some long-running operations without any I/O operations. For example, it can calculate the value of the mathematical constant Pi with many decimal places or factorials of large numbers. In such cases, there are no suspension points during which to check the coroutine cancellation status.
If you’re performing such operations in the coroutines, you may want to check the cancellation status manually to avoid useless computations and wasting resources. You can do that by checking the scope’s isActive property. There’s also ensureActive()
, which throws the CancellationException if the coroutine turns out to be cancelled. It does nothing otherwise.
Note that the CancellationException is a subclass of the Exception from the standard Kotlin library. But it doesn’t mean there’s an error in the program. It indicates the normal cancellation of the coroutine.
The uncaught exception handler ignores it and doesn’t cause the app to crash. It’s important to either not catch that kind of exception or to re-throw it after handling it in the catch block. If you catch the CancellationException and don’t rethrow it, the coroutine will continue its execution, possibly forever.
To sum up, we prefer the built-in coroutine scopes bound to the lifecycles of the Android entities. If you’re using your own scopes, don’t forget to cancel them when you no longer need them. Keep in mind that cancellation can happen only at suspension points, so you may want to manually check the cancellation status if you’re performing a long-running operation. The cancellation happens by throwing the CancellationException, so don’t forget to handle it properly.
Where to Go From Here?
You’ve just started creating performant apps that run code in the background. To learn more, including how to access data from across the internet in your app, take a look at Kodeco’s Concurrency & Networking in Android course. It contains the following modules that will give you the skills to build modern concurrent internet-enabled apps expected by end users:
- Concurrency with Coroutines in Android
- Network Requests with Retrofit in Android
- Concurrency with Kotlin Flow
The Concurrency & Networking in Android course is available now, and free for all Kodeco Personal and Kodeco for Business subscribers.
This course is for both beginning and intermediate developers. It provides a gentle on-ramp for people eager to start developing more complex Android apps that meet the expectations of today’s demanding users.
Level up your concurrency skills with Concurrency & Networking in Android today.