Summary of "Kotlin Coroutine (High-quality Course)"
Course overview (Kotlin Coroutines)
- A single ~2-hour Kotlin coroutines course split into 5 chapters, starting from fundamentals and progressing to more advanced coroutine internals.
- Teaching style: slides + demos, and the demo source code is provided on GitHub.
-
Focus areas across chapters:
- Basics: what coroutines are, coroutine builders (launch, async, runBlocking), suspending functions, and differences vs threads.
- Deeper coroutine builders: launch vs async vs runBlocking, GlobalScope vs local scope, and Job/Deferred control.
- Cancellation & exceptions: cooperative cancellation,
cancel,join,cancelAndJoin, handling CancellationException, timeouts. - Composing suspending functions: sequential vs concurrent vs lazy execution.
- Advanced fundamentals: CoroutineScope, CoroutineContext, Dispatchers, including thread behavior.
Chapter 1: Fundamentals + threading vs coroutines
Problem with threads on the main thread
- The main thread handles lightweight UI and logic.
- Heavy work on the main thread (e.g., network, file upload/download, DB queries, image loading) blocks it → the app freezes/hangs and may crash after long blocking.
Why coroutines
- Instead of spawning many expensive background threads, coroutines allow many “lightweight” concurrent tasks on fewer threads.
- Coroutines are often described as “lightweight threads”, but they are not the same as threads:
- Coroutines are “almost free” (you can create thousands without the same memory cost).
First Kotlin project setup
- Create a Kotlin/JVM project using Gradle (coroutines require dependencies via Gradle/Maven).
Key runtime behaviors: Thread vs Coroutine
- Threads: main waits for background threads to complete (as observed in demos).
- Coroutines: launching a coroutine doesn’t automatically make
mainwait for it to finish.
First coroutine demo
- Create a coroutine using
GlobalScope.launch { ... }. - Issue: coroutine output may not appear because the program exits before the coroutine completes.
- Fix: shown in the demo by increasing the waiting time (e.g., adding
delayso the coroutine can finish).
Blocking vs non-blocking delay
Thread.sleep(...)blocks the thread (bad if multiple coroutines share that thread).- Use
delay(...)(fromkotlinx.coroutines):delaysuspends the coroutine without blocking the thread.- The underlying thread may change after suspension; the coroutine can resume elsewhere.
suspend and suspending function rules
- A function marked with
suspendcan only be called from a coroutine or another suspending function. - If calling suspending code from normal code (like
main), wrap it withrunBlocking { ... }.
launch vs runBlocking (demonstrated)
launch: creates a coroutine that does not block the current thread.runBlocking: blocks the calling thread (used to bridge suspending code in non-coroutine contexts).
How to write and structure code
- Demonstrated that moving
runBlockingto wrap the wholemainchanges what runs on the main thread (scope rules).
Creating custom suspending functions
- Example idea: write
suspend fun mySuspendFunc(...) { delay(...) }and call it from a coroutine.
Chapter 2: Coroutine builders, scope, and Job/Deferred
Coroutine builders (main ones)
launchasyncrunBlocking
What “coroutine builders” are
- Functions used to create coroutines.
GlobalScope vs local scope
GlobalScope.launch/async:- The coroutine lives at the application/global level, surviving beyond destroyed screens/scopes.
- Local scope:
- Coroutine lifetime is tied to the parent scope (e.g., when a UI screen is destroyed, its child coroutine is destroyed too).
- Warning: GlobalScope is discouraged unless you explicitly want application-wide lifetime (risk of forgotten background work consuming memory).
launch: Job control
launchreturns aJob.- Control mechanisms:
- Use
job.join()to wait for completion (instead of ad-hoc sleeps). - Cancel with
job.cancel()(details covered in the cancellation chapter).
- Use
async: Deferred results
asyncreturns aDeferred<T>(extendsJob).- Retrieval:
await()waits and returns the computed result (promise-like behavior).
join()waits but doesn’t provide the value.await/joinare suspending, so they must be called within coroutine scope.
runBlocking: practical use for tests
- Main purpose stated: testing suspending functions.
- Demo:
- A test method annotated with
@Testcan’t call a suspending function directly. - Use
runBlockingto wrap the suspending call so the test can run it.
- A test method annotated with
Non-blocking vs blocking summary
launch/async: non-blockingrunBlocking: blocking (blocks the thread it’s called on)
Chapter 3: Cancellation, cooperative cancellation, exceptions, timeouts
Why cancel coroutines
- Cancel when you no longer need results or tasks take too long.
Cancellation is cooperative
- Calling
cancel()only stops coroutines that are cooperative. - Demo concept:
- If a coroutine body doesn’t call cancellable suspending functions, cancellation may not take effect immediately, so
join()waits for full completion.
- If a coroutine body doesn’t call cancellable suspending functions, cancellation may not take effect immediately, so
Cooperative cancellation approach #1: cancellable suspending functions
- Use
kotlinx.coroutinesfunctions such as:delay(...),yield(...), etc.
- These create cancellable points where CancellationException can be thrown.
Cooperative cancellation approach #2: manual checks
- Check
isActivein a loop:- When cancelled,
isActivebecomes false and the loop can terminate.
- When cancelled,
- Alternative shown:
return@launchto exit the coroutine early on cancellation.
Cancellation APIs
cancel()+join()together (and discussed behavior when cancellation doesn’t interrupt immediately).cancelAndJoin()combines both behaviors.
Handling CancellationException
- When cancelled via cancellable suspending functions, CancellationException is thrown.
- try/catch/finally behavior demonstrated:
- Code in
finallymay not run if the coroutine is already cancelled and a suspending function is called inside it. - If you must run suspending work in
finally, use:withContext(NonCancellable)
- Code in
Custom cancellation messages
- Cancel with a message:
job.cancel(CancellationException("message"))
- Catch and read
ex.message.
Timeouts
withTimeout(time):- Throws TimeoutCancellationException (a subclass of CancellationException) if time exceeds.
- Requires try/catch/finally in the demo.
withTimeoutOrNull(time):- Returns a value or null if timed out (no exception thrown).
- Requires a nullable variable type.
Chapter 4: Composing suspending functions (sequential, concurrent, lazy)
Default is sequential
- In a parent coroutine (e.g.,
runBlocking), calling suspending functions directly executes sequentially. - Demonstrated with
measureTimeMillis:- Two
delay(1s)calls → ~2s total.
- Two
Make it concurrent
- Wrap independent suspending calls with
async:- Start both coroutines in parallel.
- Retrieve results with
await().
- Demonstrated time improvement:
- Two 1s operations complete in ~1s total (concurrent execution).
Lazy start optimization
- Problem shown:
- If you call
asyncbut never use the result, you can waste work.
- If you call
- Solution:
- Use
async(..., start = CoroutineStart.LAZY) - The coroutine doesn’t start until
await()is called.
- Use
- Demonstrated that if the result is never used, the async blocks never execute.
Chapter 5: CoroutineScope, CoroutineContext, Dispatchers (threading model)
CoroutineScope
- Each coroutine is associated with a CoroutineScope.
- Using
thisinside coroutine lambdas can access theCoroutineScope. - Parent/child coroutines have distinct
CoroutineScopeobjects.
CoroutineContext
- Each coroutine also has a CoroutineContext.
- Unlike scope, some context properties can be inherited from parent to child.
- Key components emphasized:
- Dispatcher: determines which thread the coroutine runs on
- Job: cancellation/control
- Coroutine name (can also be included for debugging)
Dispatcher behavior (thread confinement vs not)
- Without an explicit dispatcher parameter (described as “confined” in the lecture):
- It inherits parent context.
- Runs on the same thread as the parent (e.g., main thread under
runBlocking).
Dispatchers.Default:- Application-level background execution behavior (contrasted with
GlobalScope.launch). - After
delay, resumption may occur on different threads.
- Application-level background execution behavior (contrasted with
Dispatchers.Unconfined:- Inherits parent initially, but after suspension (e.g.,
delay) it may resume on another thread. - Explained as “unconfined” (not tied to a single thread).
- Inherits parent initially, but after suspension (e.g.,
- Passing
coroutineContextexplicitly:- Children inherit the parent’s context so thread behavior remains aligned.
Dispatcher types mentioned
- Default, Unconfined, Main, IO
- Notes:
Mainfor UI-related operations (Android-related mention)IOfor IO operations (Android tutorial planned)
Main speakers / sources
- Speaker: Sriyank Siddhartha (course creator/host)
- Underlying technology source: Kotlin +
kotlinx.coroutineslibrary (referenced throughout; not a separate speaker)
Category
Technology
Share this summary
Is the summary off?
If you think the summary is inaccurate, you can reprocess it with the latest model.
Preparing reprocess...