Coroutines & Suspend Functions
Write asynchronous code that reads like synchronous code. Use suspend functions, coroutine scopes, and dispatchers (IO, Main, Default).
Coroutines & Suspend Functions is a free Android Academy lesson on CoddyKit — lesson 3 of 6. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Android Academy learning path, one of 6 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Problem: Blocking the UI Thread
Android runs all UI work on the main thread. If you do a network request or database read on the main thread, the UI freezes until it finishes.
If the UI freezes for more than 5 seconds, Android shows an ANR (App Not Responding) dialog and the user can force-close your app.
What Are Coroutines?
Kotlin Coroutines are a way to write asynchronous code that looks sequential — no callbacks, no RxJava chains.
- Lightweight — thousands of coroutines can run concurrently
- Suspendable — a coroutine can pause and resume without blocking a thread
- Structured — they respect a lifecycle (scope)
suspend Functions
A function marked suspend can pause its execution without blocking the thread. It can only be called from another suspend function or a coroutine scope.
import kotlinx.coroutines.*
suspend fun fetchData(): String {
delay(1000) // simulates 1 second network call
return "Data loaded!"
}
fun main() = runBlocking {
println("Fetching...")
val result = fetchData() // suspends here, doesn't block
println(result) // Data loaded!
println("Done")
}Coroutine Scopes
Every coroutine runs inside a scope that defines its lifecycle:
viewModelScope— cancelled when ViewModel is clearedlifecycleScope— cancelled when Activity/Fragment is destroyedGlobalScope— lives forever (avoid this)runBlocking— for testing only
launch vs async
Two ways to start a coroutine:
launch { }— fire-and-forget, no return valueasync { }.await()— returns a result you can wait for
import kotlinx.coroutines.*
fun main() = runBlocking {
// launch: no return value
launch {
delay(500)
println("launch done")
}
// async: returns a Deferred<T>
val deferred = async {
delay(300)
42
}
val result = deferred.await()
println("async result: $result")
}Dispatchers: Which Thread?
Dispatchers control which thread(s) the coroutine runs on:
Dispatchers.Main— UI thread (for updating views)Dispatchers.IO— for network and disk operationsDispatchers.Default— for CPU-intensive work
Use withContext(Dispatchers.IO) { } to switch dispatcher inside a coroutine.
viewModelScope Example
Fetch data in a ViewModel without blocking the UI:
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*
class DataViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// Switch to IO thread for network
val data = withContext(Dispatchers.IO) {
// simulate network request
delay(1000)
"Result from server"
}
// Back on Main thread — safe to update UI
_uiState.value = data
}
}
}Error Handling in Coroutines
Handle exceptions inside a coroutine with try/catch, just like regular code:
viewModelScope.launch {
try {
val result = withContext(Dispatchers.IO) {
api.fetchUser() // may throw IOException
}
_user.value = result
} catch (e: Exception) {
_error.value = "Failed: ${e.message}"
}
}Parallel Coroutines
Run two tasks in parallel and wait for both to finish using async/await:
viewModelScope.launch {
// Both run at the same time!
val userDeferred = async(Dispatchers.IO) { api.getUser() }
val postsDeferred = async(Dispatchers.IO) { api.getPosts() }
val user = userDeferred.await()
val posts = postsDeferred.await()
_state.value = UiState(user, posts)
}Quick Check
Which coroutine dispatcher should you use for network and database operations?
Recap: Coroutines
You can now write async code without callbacks:
suspend fun— can pause without blocking the threadviewModelScope.launch { }— start a coroutine in a ViewModelwithContext(Dispatchers.IO)— switch to a background threadasync { }.await()— parallel work with a result- try/catch works normally inside coroutines
Next: the Repository pattern — clean separation between data sources.
Frequently asked questions
Is the “Coroutines & Suspend Functions” lesson free?
Yes — the full text of “Coroutines & Suspend Functions” is free to read here on the web, and the Android Academy course includes 6 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Android Academy course, upgrade to CoddyKit PRO.
What will I learn in “Coroutines & Suspend Functions”?
Write asynchronous code that reads like synchronous code. Use suspend functions, coroutine scopes, and dispatchers (IO, Main, Default). You practise Android Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Android Academy?
No prior experience is required. Android Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 6, so you can start here or from the beginning and move at your own pace.
How long does the “Coroutines & Suspend Functions” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Android Academy lesson?
Yes. Every Android Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- ViewModel & LiveData
- Room Database
- Coroutines & Suspend Functions
- Repository Pattern
- Navigation Component
- Dependency Injection with Hilt