Thursday, August 7, 2025

Kotlin Coroutines & Flow in Android

 

1. Introduction

Kotlin Coroutines are a core part of modern Android development. They provide a concise, safe, and efficient way to handle asynchronous tasks such as network calls, database operations, and UI updates. Combined with Flow, coroutines enable reactive, stream-based data handling that integrates seamlessly with Jetpack components.

This document introduces key coroutine concepts and demonstrates best practices for using Coroutines + Flow in real Android projects.


2. Why Coroutines?

Before coroutines, Android developers relied on:

  • Callbacks (hard to read, callback hell)

  • AsyncTask (deprecated)

  • RxJava (powerful but complex)

Coroutines solve these problems by:

  • Writing async code in a sequential style

  • Providing structured concurrency

  • Integrating deeply with Android lifecycle components


3. Core Coroutine Concepts

3.1 suspend functions

A suspend function can pause execution without blocking a thread.

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

Key points:

  • Can only be called from another suspend function or coroutine

  • Does not block the main thread


3.2 CoroutineScope

A CoroutineScope defines the lifecycle of coroutines.

Common scopes in Android:

  • viewModelScope – tied to ViewModel lifecycle

  • lifecycleScope – tied to Activity / Fragment lifecycle

viewModelScope.launch {
    val user = fetchUser()
}

3.3 Dispatchers

Dispatchers define which thread the coroutine runs on:

  • Dispatchers.Main – UI operations

  • Dispatchers.IO – network / disk I/O

  • Dispatchers.Default – CPU-intensive work

withContext(Dispatchers.IO) {
    repository.loadFromNetwork()
}

4. Structured Concurrency

Structured concurrency ensures that:

  • Child coroutines are cancelled with their parent

  • No background work leaks after lifecycle ends

viewModelScope.launch {
    val a = async { loadA() }
    val b = async { loadB() }
    combine(a.await(), b.await())
}

Benefits:

  • Safer cancellation

  • Easier error handling


5. Error Handling

5.1 try-catch

viewModelScope.launch {
    try {
        repository.fetchData()
    } catch (e: Exception) {
        handleError(e)
    }
}

5.2 CoroutineExceptionHandler

Use for top-level coroutines:

val handler = CoroutineExceptionHandler { _, throwable ->
    logError(throwable)
}

viewModelScope.launch(handler) {
    repository.fetchData()
}

6. Introduction to Flow

Flow represents a cold asynchronous data stream.

Typical use cases:

  • Database updates

  • UI state streams

  • Continuous network polling

fun observeUsers(): Flow<List<User>> = flow {
    emit(api.getUsers())
}

7. Collecting Flow Safely

7.1 In ViewModel

val users = repository.observeUsers()
    .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5_000),
        initialValue = emptyList()
    )

7.2 In UI (Lifecycle-aware)

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.users.collect { list ->
            render(list)
        }
    }
}

8. Flow Operators (Commonly Used)

  • map – transform data

  • filter – filter emissions

  • combine – merge multiple flows

  • debounce – handle rapid events

searchQuery
    .debounce(300)
    .distinctUntilChanged()
    .flatMapLatest { query ->
        repository.search(query)
    }

9. Best Practices

✅ Use viewModelScope for business logic

✅ Keep suspend functions small and focused

✅ Use Flow for continuous data, suspend for one-shot calls

✅ Avoid launching coroutines in repositories without a scope owner

❌ Do not use GlobalScope


10. Conclusion

Kotlin Coroutines and Flow are essential tools for building clean, scalable, and lifecycle-safe Android applications. When used correctly, they reduce boilerplate, improve readability, and prevent common concurrency bugs.

Mastering coroutines is a key step toward becoming a senior Android developer in modern Kotlin-based projects.


Author: Mobile Team
Topic: Kotlin Coroutines & Flow
Target: Android Developers