Skip to main content

Kotlin Coroutines Basics

Async programming on Android the modern way — suspend, scopes, dispatchers.

App DevelopmentIntermediate 13 min read 5 lessons
KotlinAndroid

What You'll Learn

  • Understand what coroutines are and why they replace AsyncTask
  • Use suspend functions and launch / async builders
  • Pick the right Dispatcher for each job
  • Use viewModelScope and lifecycleScope safely
  • Handle errors with try/catch and CoroutineExceptionHandler

Prerequisites

  • Kotlin basics (data classes, lambdas, extension functions)
  • Basic Android / ViewModel knowledge

Step-by-Step Guide

  1. Step 1

    1. What Are Coroutines?

    Coroutines are suspendable computations. Unlike threads, they're lightweight — you can run thousands concurrently on a handful of OS threads. A suspend function can pause without blocking the thread, letting other work proceed. The Kotlin compiler transforms suspend functions into state machines under the hood. Add dependencies: implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.x'

  2. Step 2

    2. launch vs async

    launch starts a coroutine and returns a Job (fire-and-forget). async starts a coroutine and returns a Deferred<T> — await() the result when needed. Use launch for operations with no return value (writing to DB, logging). Use async + await when you need a result or want to run multiple operations concurrently and combine results.

  3. Step 3

    3. Dispatchers

    Dispatchers.Main: UI updates only — runs on the Android main thread. Dispatchers.IO: network calls, file/database I/O (optimized pool of 64 threads). Dispatchers.Default: CPU-intensive work (sorting, parsing). Switch with withContext(Dispatchers.IO) { ... } inside a coroutine without blocking. Never do network I/O on Dispatchers.Main.

  4. Step 4

    4. Structured Concurrency with Scopes

    Always launch coroutines in a scope. viewModelScope cancels all coroutines when the ViewModel is cleared — no memory leaks. lifecycleScope (in Activity/Fragment) respects the lifecycle. GlobalScope is almost always wrong — it ignores structured concurrency. If a child coroutine fails, the scope propagates the failure and cancels siblings.

  5. Step 5

    5. Error Handling

    Wrap suspend calls in try/catch just like synchronous code. For launch, unhandled exceptions propagate to the CoroutineExceptionHandler installed on the scope. For async, exceptions are thrown when you call await() — wrap that in try/catch. CancellationException is special — don't catch and swallow it, as that breaks cancellation. Rethrow it if you catch Throwable.

Further Reading & Resources

Ready to put this into practice?

Apply for a real App Development internship and ship production projects with mentorship.

Related Tutorials