
Clone the sample project from GitHub via Android Studio's git client, switch to Curtin's Course Complete, Coroutines Course Empty Exercises, or Coroutine course empty, and build and run the app.
Explore approaches to asynchronous programming, compare callbacks, RxJava, and Kotlin coroutines, weigh the benefits and drawbacks, and see why Kotlin coroutines are the current best approach for Android.
Implement Kotlin coroutines in the ViewModel to perform sequential network requests with suspend functions and Retrofit, using the ViewModel scope and launch, while handling loading state and errors.
Explore how Kotlin coroutines simplify asynchronous programming for Android development, offering comprehensible, imperative-style code with familiar constructs, high flexibility, and first-class support from Google and JetBrains.
Master the basics and theory of Kotlin Coroutines, comparing them to regular routines, exploring suspend functions, non-blocking behavior, and the Coroutines library source code behind delay.
IMPORTANT: Unfortunately, the playground functions that are located under:
com.lukaslechner.coroutineusecasesonandroid.playground
stopped working with the introduction of Android Studio Otter.
They work, however, when they are located in a separate Kotlin Gradle module.
That's why I created a new Kotlin Gradle module and moved the playground files there!
Please pull the latest version of the repository to have the latest changes!
Explore the difference between routines and coroutines by comparing ordinary functions with suspend functions and delay, and see how cooperative coroutines share control flow.
Explore suspend functions in Kotlin coroutines, learn how they perform long running operations without blocking threads, how to call them from coroutines or async blocks, and recognize suspension points.
Highlight blocking and suspending in Kotlin coroutines by showing how Thread.sleep blocks threads, while delay suspends without blocking the main thread.
Explore multithreaded coroutines, switch threads with withContext and default dispatcher, and offload blocking calls to background threads to prevent UI freezing.
Recap the core concepts of Kotlin coroutines and Android dependencies, including suspend functions, launch and async, main safety, Retrofit usage, and basic error handling with try-catch.
Implement use case #3 by sequentially loading Android version features with a ViewModel and loading indicator, then optimize by performing network requests concurrently using async in coroutines.
Implement a sequential use case that fetches recent Android versions from the Android versions endpoint and loads each version's features via sequential network requests, illustrating Kotlin coroutines and map operator.
Implement use case four concurrently to run Android version feature requests in parallel, reducing total network time from about 4000 ms to about 2000 ms using Kotlin coroutines.
Demonstrates launching a ViewModel-scoped coroutine, using async and awaitAll to fetch Android version features in parallel, reducing load time from four to two seconds and updating the UI.
Learn to implement timeouts and retries for network requests using Kotlin coroutines, via with timeout and with timeout or null, including UI input and timeout handling.
Learn to implement retry logic for network requests in Kotlin coroutines using repeat, with two retries, and error handling via try-catch and Timber in loadRecentAndroidVersions with a mock api.
Add exponential backoff to the retry function by introducing initial delay, max delay, and a doubling factor, using a suspend delay between retries.
Perform two parallel network requests for Android Oreo API 27 and Pie API 28 features. Use timeout and optional retries, combining these concepts to display the data in the UI.
Demonstrate a retry-with-timeout solution using coroutines, running parallel fetches for Android version features, handling timeouts and retries toward a successful response.
Implement a room database cache to load Android versions from database or network, update the UI with status icons, and provide a clear database option.
Connect your app to a room database using suspend functions in data access objects to store and load data, leveraging Android Jetpack and coroutine support with libraries like WorkManager.
Learn to use coroutines to perform background operations, switch between contexts, and choose dispatchers to keep your app responsive.
Learn to implement a factorial calculation in a background coroutine inside a ViewModel, measure computation and string conversion durations, and update the UI with a success state.
Understand how the coroutine context governs execution by detailing dispatchers, the job, and the error handler within the ViewModel scope, including main, a background dispatcher, and structured concurrency.
Explore the main, IO, and default coroutine dispatchers for Android, and learn when to use each, including creating custom dispatchers with new single thread context.
Explains how to use withContext for safe context switching in coroutines, moving CPU-heavy work to the default dispatcher and returning to the main thread to update the UI without freezing.
Distribute factorial calculation across multiple coroutines by dividing into subranges and running them in parallel, then multiply subresults to obtain factorial and convert result on a background thread.
Implement a parallel factorial calculator with Kotlin coroutines, using withContext to switch to the default dispatcher, async subrange tasks, awaitAll, and fold to the final result.
Master structured concurrency to cancel coroutines when users leave the screen and handle partial network failures without crashing.
Explore structured concurrency in Kotlin coroutines by starting coroutines in a logical scope, forming parent-child hierarchies, and handling cancellations and exceptions to prevent resource waste in Android apps.
Explore structured concurrency by creating a scope in Android Studio, launching coroutines, and canceling the scope to end the lifetime of child jobs.
Explore how coroutines form a hierarchy within a scope by creating and linking jobs, launching child coroutines, and understanding how context elements shape the parent child relationship in structured concurrency.
Explore Kotlin coroutines and Flow by seeing how a parent job waits for all child coroutines to finish, using join, runBlocking, and delays to demonstrate task completion.
Learn how structured concurrency handles cancellation: cancel a parent to cancel all children, while canceling a single child won’t affect its parent or siblings, using the cancel and join function.
Explore exception propagation and job hierarchy in Kotlin coroutines, comparing ordinary jobs and supervisor jobs. Ordinary jobs cancel siblings and the scope on failure; supervisor jobs do not.
Avoid the global scope to prevent unbounded lifetimes of top level coroutines and rely on lifecycle scopes like ViewModel for automatic cancellation.
Launch coroutines in the ViewModel scope to fetch the recent Android versions from a mock API, and cancel them when the ViewModel is cleared, using a supervisor job and dispatchers.main.immediate.
Explore scoping functions for parallel decomposition using coroutineScope and supervisorScope, installing child coroutines with proper cancellation behavior. Learn when to use coroutineScope for cascading cancellations and supervisorScope for independent tasks.
Explore how Kotlin coroutines handle cancellation, using a sample that launches a coroutine, delays, and is canceled, showing cancellation exceptions and cooperative cancellation across suspend functions.
Use withContext with the non-cancellable job to run cleanup code after cancellation, allowing suspend calls like delay and preventing cancellation exceptions during a critical section.
Demonstrates making a factorial calculation cooperative with cancellation in a ViewModel by introducing a calculation job, a cancel button, and using yield to periodically check activity, preventing memory leaks.
Master cooperative cancellation in Kotlin coroutines by using ensureActive or yield, checking isActive in custom suspend functions, and guarding cleanup with context and a non-cancellable job.
Google recommends Kotlin Coroutines and Flow as the preferred solution for asynchronous programming on Android. Sooner or later, probably every Android developer will get in touch with these topics.
This course will give you a deep understanding of Kotlin Coroutines and Flow and show how to implement the most common use cases for Android applications.
This course consists of two big parts: The Coroutines part and the Flow part.
Before being able to use Flows in our applications, we first need a solid understanding of Coroutines. That’s why Coroutines are covered first. However, if you already have some experience with Coroutines, then you can also start with the Flow part right away, and jump back to lessons of the Coroutines part whenever needed.
In the part about Coroutines, first, we will take a detailed look at the advantages of Kotlin Coroutines over other approaches for asynchronous programming, like RxJava and Callbacks.
Then, we will talk about some theoretical fundamentals. These include:
Routines vs. Coroutines
Suspend Functions
Coroutines vs. Threads
Blocking vs. Suspending
Multithreaded Coroutines
Internal workings
Next, we will implement some of the most common use cases for Kotlin Coroutines in Android applications. These include:
Performing network requests with Retrofit sequentially and concurrently
Implementing Timeouts and Retries
Using Room with Coroutines
Performing background processing with Coroutines
Continuing Coroutine execution even when the user leaves the screen.
To improve your learning experience, this course also challenges you with several exercises.
Learning Coroutines can be overwhelming because you need to learn a lot of new concepts. However, we are going to start simple and as our use cases will get more and more complex, we will learn about new concepts step-by-step. These new concepts are:
Coroutine Builders (launch, async, runBlocking)
Coroutine Context
Coroutine Dispatchers
Structured Concurrency
Coroutine Scopes (viewModelScope, lifecycleScope, GlobalScope)
Jobs and SupervisorJobs
scoping functions (coroutineScope{} and supervisorScope{})
Cooperative Cancellation
Non-Cancellable Code
We will also make a deep dive into Exception Handling and discuss concepts like:
exception handling with try/catch
exception handling with CoroutineExceptionHandlers
when to use try/catch and when to use a CoroutineExceptionHandler
exception handling in Coroutines started with launch and async
exception handling specifics of scoping functions coroutineScope{} and supervisorScope{}
Unit Tests are very important for every codebase. In the course's final section, we will write unit tests for most of the coroutine-based use cases we implemented earlier. We will discuss concepts like
TestDispatchers (StandardTestDispatcher, UnconfinedTestDispatcher)
Creating a JUnit4 Rule for testing coroutine-based code
runTest{} Coroutine Builder
virtual time
Testing sequential and concurrent execution
TestScopes
Updated coroutine testing module using the latest APIs (2026)
In the part about Kotlin Flow, we first cover all the basics. We will answer the question “What is a Flow?” and then we discuss the benefits and drawbacks of reactive programming.
Afterward, we are going to have a look at different Flow builders and operators:
basic flow builders
terminal operators
terminal operator “launchIn()”
terminal operator “asLiveData()”
lifecycle operators
intermediate operators
In our first real Flow use case, we use a Flow to create a live stock-tracking feature, that uses all the available basic flow components.
In the next module, we will take a look at Exception Handling and Cancellation with Kotlin Flow.
In the following module, you will learn about StateFlow and SharedFlow and the following concepts:
how to make Coroutines lifecycle-aware with the “repeatOnLifecycle()” suspend function
Hot Flows VS Cold Flows
Converting Flows to SharedFlows with the “shareIn()” operator
Converting Flows to StateFlows with the “stateIn()” operator
When to use SharedFlow and when to use StateFlow
Next, you will learn about Channels, how they differ from hot flows, and when they are useful in Android Applications.
By the end of this course, you will have a fundamental understanding of Kotlin Coroutines and Flows and be able to write readable and maintainable, asynchronous, and multithreaded Android Applications.