Asynchronous programming and concurrency in Android (modern best practices)
Kotlin Coroutines: Effortless async programming
Master coroutines for clean, efficient concurrency without callbacks or threads.
Getting Started with Coroutines (Absolute Basics)
Zero-prereq intro: mental model, first coroutine, basic suspend, and core terms.
3 days
- play_circleWhat Are Coroutines? (not threads) - the mental model
- play_circleHello Coroutine: runBlocking + launch + delay (console demo)
- play_circlesuspend Functions 101: writing & calling your first suspending API
- articleCheat Sheet: coroutine • job • scope • builder - quick definitions
- articleProject Setup (JVM & Android): coroutines-core/android + test libs
- assignmentPractice: two coroutines printing → refactor into a suspending function
Suspending & Builders - Foundations
Clear separation of concepts: suspension vs blocking, builders, scope boundaries, and a first taste of structured concurrency.
5 days
- play_circleSuspension vs Blocking: delay() vs Thread.sleep()
- play_circleSuspension Points in Practice: delay, I/O with withContext(Dispatchers.IO), Flow collect/emit
- play_circleCooperative Cancellation: isActive, ensureActive, yield (keep work responsive)
- play_circleBuilders Deep Dive: launch vs async/await (returning results)
- play_circlecoroutineScope vs supervisorScope: structure & child-failure behavior
- play_circleStructured Concurrency 101 (overview): parents, children, Job trees
- play_circlerunBlocking - when it's OK (tests/main) and when it's not (Android/UI)
- articleCheat Sheet: choosing a builder & scope (quick reference)
- assignmentPractice: Parallel API calls with async + cancellation propagation
Structured Concurrency - Deep Dive
Principles, job hierarchies, failure propagation, supervision, and real-world scope design.
5 days
- play_circleStructured Concurrency Principles (the key invariants)
- play_circleJob Hierarchies & Cancellation Trees (visual walkthrough)
- play_circleFailure Propagation: launch vs async (who cancels whom?)
- play_circleSupervisorJob vs supervisorScope: boundaries & use-cases
- play_circleStructured Parallelism: async + awaitAll + cancellation on first failure
- play_circleMultiple Failures, finally, and NonCancellable (cleanup patterns)
- play_circleAnti-patterns & Leaks: GlobalScope, orphan Jobs, ad-hoc scopes
- articleScope Design Guide (UI, repo, use-case): who owns the scope?
- assignmentPractice: Cancellation propagation lab (normal vs supervisor)
- assignmentPractice: Parallel fan-out with fail-fast & proper cleanup
Context & Dispatchers - Mastery
Understand CoroutineContext deeply: elements, inheritance, dispatchers (Main/IO/Default/Unconfined), thread switching with withContext, limited parallelism, and thread-local propagation.
5 days
- play_circleCoroutineContext 101: elements, + combination, inheritance & overriding
- play_circleDispatchers: Main, IO, Default - when to use each (and why Unconfined is risky)
- articleAndroid specifics: Dispatchers.Main.immediate & main-safety notes
- play_circlewithContext correctly: switching threads without nesting traps; cancellation semantics
- play_circleTuning concurrency: Dispatchers.IO.limitedParallelism(n) and when to use it
- articleCustom dispatchers: Executor.asCoroutineDispatcher vs newSingleThreadContext (trade-offs & cleanup)
- play_circleNaming & diagnostics: CoroutineName + structured logging
- play_circleThread context propagation: ThreadLocal.asContextElement / ThreadContextElement (MDC, request IDs)
- articleQuick note: CoroutineExceptionHandler is a context element (limitations for async)
- assignmentPractice: Reduce context hops, add CoroutineName, wire MDC, and cap IO parallelism
CoroutineStart Modes - Practical Control
DEFAULT, LAZY, ATOMIC, UNDISPATCHED: how they start, when to use them, and common pitfalls.
3 days
- play_circleThe start parameter on launch/async - what it controls
- play_circleDEFAULT vs LAZY - triggering start (start/join/await) & pitfalls in structured concurrency
- play_circleUNDISPATCHED - run on caller thread until first suspension (safer than Unconfined)
- articleATOMIC (Delicate) - guaranteed start before cancellation: use-cases & warnings
- assignmentPractice: Compare modes - warm-up jobs, lazy pipelines, and observing thread hops
Cancellation & Timeouts - Real-World Patterns
Cooperative cancellation model, robust cleanup, timeouts that don't bite you, and writing cancel-safe suspend APIs.
5 days
- play_circleCancellation Model: Job.cancel, CancellationException, parent→child rules
- play_circleCooperative Cancellation in Practice: isActive, ensureActive, yield (CPU loops)
- play_circleCleanup Patterns: try/finally, withContext(NonCancellable), closing resources/channels
- play_circleTimeouts Deep Dive: withTimeout vs withTimeoutOrNull, propagation & common pitfalls
- play_circleDesigning Cancel-Safe suspend APIs with suspendCancellableCoroutine
- articleHooking Cancellation: invokeOnCancellation, listener removal, idempotent cleanup
- play_circleCanceling Blocking Work: IO with withContext(Dispatchers.IO), interruptible ops, library caveats
- play_circlePolicy & Patterns: cancel vs retry, supervisor boundaries, time-budget design
- assignmentPractice: Build a cancel-safe repository (network + cache) with timeouts & retries
Exception Handling & Supervision - Mastery
Understand how failures propagate, where exceptions surface, how supervision isolates errors, and how to build resilient retry logic.
5 days
- play_circleException Model: child→parent propagation & who cancels whom
- play_circlelaunch vs async: where exceptions surface (handler vs await)
- play_circleCoroutineExceptionHandler: root-only behavior & limitations with async/supervision
- articleSupervisorJob vs supervisorScope: isolation boundaries & use-cases
- play_circleMultiple Failures & Cancellation: suppressed exceptions, failures during cleanup
- play_circleCancellation vs Failure: handling CancellationException vs real errors (rethrow patterns)
- play_circleRetries & Backoff (no Flow): exponential backoff, jitter, cancellation-aware delays
- assignmentPractice: Fail-fast vs supervised trees + resilient use-case with retries
Shared Mutable State & Synchronization - Practical Patterns
Avoid races with confinement, locks, semaphores, or actors. Keep critical sections tiny and responsive.
5 days
- play_circleStrategies Overview: immutability, confinement, message passing
- play_circleMutex.withLock: critical sections, tryLock, and “no blocking inside the lock”
- play_circleSemaphore.withPermit: limiting parallel work & rate limiting (vs limitedParallelism)
- articleSingle-threaded serialization: custom dispatcher/executor & cleanup duties
- play_circleActors for Single-Owner State: when an actor beats locks; mailbox/backpressure basics
- articleChoosing the Right Tool: decision guide + fairness/starvation notes
- assignmentPractice: Controlled parallel downloads + shared progress counter (lock-free → mutex)
Flow Fundamentals
Cold streams, terminal ops, cancellation transparency, and where work runs.
1 week
- play_circleCold vs Hot - mental model & lifecycle (preview of StateFlow/SharedFlow)
- play_circleflow{} builder & terminal ops: collect, first/firstOrNull, single, toList
- play_circleContext & execution: flowOn vs withContext (upstream-only) + exception transparency
- articleSide-effects & structure: onStart, onEach, onCompletion, emitAll
- play_circleWrapping callbacks with callbackFlow: awaitClose, cancellation-safe cleanup
- play_circlechannelFlow vs callbackFlow - when to choose which
- assignmentPractice: Debounced search pipeline (cold Flow)
Flow Operators & Backpressure
Transform, combine, and control rate like a pro (with correct error semantics).
5 days
- play_circleTransform basics: map, transform, transformLatest (when latest cancels prior work)
- play_circleFlatMap family: flatMapConcat / flatMapMerge / flatMapLatest - trade-offs & patterns
- play_circleCombining streams: zip vs combine (+ combineTransform) - ordering & timing
- play_circleRate control: buffer(capacity, onBufferOverflow), conflate, collectLatest, debounce, sample
- articleStateful ops: distinctUntilChanged/By, scan, runningFold
- play_circleError handling: catch placement (upstream/downstream), onCompletion, retry/retryWhen
- assignmentPractice: Performance-tuned pipeline (debounce + flatMapLatest + buffer)
Hot Flows: StateFlow & SharedFlow
UI state, one-shot events, and sharing strategies done right.
4 days
- play_circleStateFlow for UI State: replay=1, conflation, update()
- play_circleSharedFlow for Events: replay, extraBufferCapacity, onBufferOverflow (sticky vs non-sticky)
- articleTurning Cold → Hot: stateIn & shareIn with SharingStarted (Eagerly/Lazily/WhileSubscribed)
- play_circleScopes & Performance: where work runs with flowOn + stateIn/shareIn; avoid per-collector hot flows
- assignmentPractice: ViewModel exposing StateFlow (UI state) + SharedFlow (one-shot events)
Lifecycle-Aware Collection (Android)
Compose & View patterns that are safe, leak-free, and production-ready.
4 days
- play_circleLaunching in Android: viewModelScope & lifecycleScope (ownership, boundaries, pitfalls)
- play_circleCompose: collectAsStateWithLifecycle (vs collectAsState), LaunchedEffect, rememberCoroutineScope, snapshotFlow
- play_circleViews: repeatOnLifecycle (and why launchWhenX is discouraged)
- articleAvoiding Leaks & Duplicates: single source of truth, shareIn/stateIn, where to collect
- assignmentPractice: Build a lifecycle-safe screen (Compose + View example)
Channels - Pipelines & Point-to-Point
Build producer–consumer pipelines with Channels and know when SharedFlow is the better fit.
4 days
- play_circleChannel Types & Capacity: RENDEZVOUS / BUFFERED / CONFLATED / UNLIMITED
- play_circleSending & Receiving: send/receive, trySend/tryReceive, receiveCatching
- play_circleLifecycle: close vs cancel, iteration with for-loop, backpressure semantics
- articlePatterns: fan-in, fan-out, pipelines, fairness trade-offs
- play_circleChannel vs SharedFlow: decision guide (point-to-point vs broadcast)
- assignmentPractice: Multi-stage channel pipeline with multiple producers/consumers
Integration: Networking, Storage, Background
End-to-end Android patterns with coroutines: suspend networking, offline-first storage, paging, and background work.
1 week
- play_circleRetrofit suspend APIs + serialization (Moshi / Kotlinx) & OkHttp interceptors/timeouts
- play_circleNetwork patterns: withContext(Dispatchers.IO), retries/backoff, error mapping (HTTP→Domain)
- play_circleRoom DAO with Flow: queries, transactions, and DTO → Entity → Domain mapping
- articleSingle Source of Truth: cache-then-network, invalidation, and sync strategies
- play_circlePaging 3 in practice: Pager, PagingSource, RemoteMediator, load states, cachedIn(viewModelScope)
- play_circleBackground work: WorkManager CoroutineWorker (cancellation, progress, constraints) vs Foreground service
- assignmentPractice: Offline-first repository (Retrofit + Room + Paging RemoteMediator) with retry & background sync
Testing Coroutines & Flows - From Zero to Flake-Free
Deterministic, fast, and reliable tests with kotlinx-coroutines-test and Turbine.
5 days
- play_circleTest Dispatchers 101: StandardTestDispatcher vs UnconfinedTestDispatcher
- play_circlerunTest & Virtual Time: scheduler, advanceTimeBy, runCurrent, advanceUntilIdle
- articleMainDispatcherRule (JUnit4/JUnit5) & swapping Dispatchers.Main safely
- play_circleTesting suspend functions: success, exceptions, cancellation
- play_circleTesting Cold Flows: terminal ops, flowOn, catch placement
- play_circleTesting Hot Flows (StateFlow/SharedFlow): backgroundScope, replay, buffer, timing
- play_circleTurbine Patterns: awaitItem, skipItems, expectNoEvents, timeouts, cancelAndIgnoreRemainingEvents
- play_circleTime-based Operators: debounce, sample, retry/backoff with virtual time
- articleChannels & select in tests: send/receive, closing, and race scenarios
- play_circleFlake-proofing: deterministic scheduling, avoid real delays, leak checks with TestScope
- assignmentPractice: Repo Flow tests (retry + timeout) + StateFlow UI state with Turbine
Performance, Patterns & Capstones - Ship It
Make it fast and resilient, then apply everything in real projects.
1 week
- play_circleMinimize context hops & avoid needless withContext (throughput & readability)
- play_circleTuning parallelism: Dispatchers.IO.limitedParallelism(n) vs Semaphore.withPermit
- play_circleBackpressure in hot streams: StateFlow/SharedFlow buffer, overflow, replay sizing
- play_circleDetecting cancellation leaks & logging coroutine trees (diagnostics playbook)
- articlePattern catalog: retries/backoff, time budgets, circuit breaker & hedging
- play_circleCapstone A: Offline-First Search (Flow + Room + retries)
- play_circleCapstone B: Downloader (callbackFlow + StateFlow, pause/resume/cancel)
- assignmentPractice: Implement one capstone end-to-end (repo, tests, perf checks)