androidengineers.Book a session

Architecture & Data

Coroutines Basics

article40 minMedium

Coroutines let Kotlin run asynchronous work without blocking the main thread. Android apps need this for database queries, network requests, file reads, and other slow operations.

The main thread draws UI. If you block it, the app freezes.

Suspend Functions

A suspend function can pause and resume without blocking a thread.

suspend fun loadUser(): User {
    return api.getUser()
}

Suspend functions must be called from another suspend function or from a coroutine.

ViewModel Scope

In Android, ViewModels provide viewModelScope.

class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    fun loadUsers() {
        viewModelScope.launch {
            val users = repository.getUsers()
            // update state
        }
    }
}

When the ViewModel is cleared, its coroutines are cancelled automatically.

Dispatchers

Dispatchers decide where work runs.

DispatcherUse
Dispatchers.MainUI updates
Dispatchers.IOnetwork, database, files
Dispatchers.DefaultCPU-heavy work

Many modern libraries switch dispatchers internally, but understand the concept.

withContext

Use withContext to run a block on a specific dispatcher and get the result back.

suspend fun loadUsers(): List<User> {
    return withContext(Dispatchers.IO) {
        api.getUsers()
    }
}

Use it when you write your own suspend functions that need to move to a background thread. Most Retrofit and Room calls handle this for you, but you will write withContext often in repositories.

Flow

Flow is a stream of values over time. Instead of getting a list once, a Flow keeps emitting new values whenever the underlying data changes.

// DAO returns a Flow — it emits a new list every time the database changes
@Query("SELECT * FROM tasks")
fun observeTasks(): Flow<List<TaskEntity>>

In the ViewModel, collect the Flow into a StateFlow that the UI observes:

val tasks: StateFlow<List<Task>> = repository.observeTasks()
    .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = emptyList()
    )

stateIn converts the Flow to a StateFlow that holds the latest value and replays it to new collectors. You will see this pattern in almost every production ViewModel.

Handling Errors

Always handle failures in launched coroutines.

viewModelScope.launch {
    state = state.copy(isLoading = true)
    try {
        val data = repository.loadData()
        state = state.copy(items = data, isLoading = false)
    } catch (e: Exception) {
        state = state.copy(error = e.message, isLoading = false)
    }
}

Practice

Create a fake repository with a suspend function that delays for one second and returns a list of lessons. Load it from a ViewModel and show loading state.

Summary

Coroutines are the foundation of modern Android async work. Learn suspend, launch, withContext, viewModelScope, dispatchers, Flow, StateFlow, and error handling. These concepts connect every layer of the app — from the database to the UI.

YOUR LEARNING JOURNEY

0 of 22 available lessons completed

Progress saved in this browser. No account needed.
Coroutines Basics | Junior Android Developer | Android Engineers