← Back to list

Understanding Coroutine Cancellation in Kotlin — Complete Guide with Real Android Examples

When Android developers start using Kotlin Coroutines, most learn:

Arindam Ghosh · 2026-05-29 05:03 · 0 claps · 3.1 min read
#kotlin-coroutines #cancel-coroutine #supervisor-jobs #kotlin-beginners
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

Understanding Coroutine Cancellation in Kotlin — Complete Guide with Real Android Examples

When Android developers start using Kotlin Coroutines, most learn:

viewModelScope.launch { }

But very few truly understand how coroutine cancellation works internally.

And cancellation is one of the most important concepts in real-world Android apps.

Without proper cancellation handling:

  • Multiple API calls run unnecessarily
  • Memory leaks happen
  • Old search results overwrite new ones
  • Background tasks continue after screen destruction
  • App performance suffers

In this article, we’ll deeply understand:

  • Job cancellation
  • Scope cancellation
  • Parent-child cancellation
  • coroutineScope
  • supervisorScope
  • Cooperative cancellation
  • CancellationException
  • Timeout cancellation
  • Real production use cases

What is Coroutine Cancellation?

Coroutine cancellation means:

Stopping a coroutine before it completes.

Coroutines are designed to be lightweight and lifecycle-aware.

Unlike Threads, coroutines support structured concurrency and automatic cancellation propagation.

1. Job Cancellation

The simplest type of cancellation.

private var loadJob: Job? = null

fun loadData(){
  loadJob?.cancel()

  loadJob = viewModelScope.launch{

  delay(500)

  println ("API Completed")

  }
}

What happens here?

When loadData() is called again:

  • Previous coroutine gets cancelled
  • New coroutine starts

Real Android Use Case

Perfect for:

  • Search APIs
  • Retry button
  • Pagination
  • Debounce
  • Latest-request-only architecture

Example: Search API

private var searchJob: Job? = null

fun search(query: String) {
    searchJob?.cancel()

    searchJob = viewModelScope.launch {

        delay(500)

        repository.search(query)
    }
}

Why?

If user types:

a
an
and
andr
andro
android

Without cancellation:

  • 6 API calls happen

With cancellation:

  • Only latest request executes

This is a real production pattern.

2. Scope Cancellation

When a scope cancels:

  • All child coroutines cancel automatically.

Example:

viewModelScope.launch {

launch {
        delay(5000)

        println("Task 1")
    }
    launch {
        delay(5000)

        println("Task 2")
    }
}

If:

viewModelScope.cancel()

Both child coroutines cancel.

Why This Matters

This is called:

Structured Concurrency

Kotlin ensures:

  • parent controls children
  • no orphan coroutines
  • safer concurrency

3. Parent-Child Cancellation

By default:

If one child fails, all siblings cancel.

Example:

coroutineScope {

    launch {
        throw Exception("API Failed")
    }

    launch {
        delay(5000)

        println("Second API")
    }
}

Result:

  • Second coroutine also cancels

Why?

Because coroutineScope follows structured concurrency strictly.

This prevents inconsistent state.

4. supervisorScope

One of the most important coroutine concepts.

Example:

supervisorScope {

launch {
        throw Exception("API Failed")
    }
    launch {
        delay(5000)

        println("Second API Still Running")
    }
}

Now:

  • First coroutine fails
  • Second coroutine survives

Real Android Example

Imagine:

  • Coin API fails
  • Pokemon API succeeds

You still want partial UI data.

Example:

supervisorScope {
val coinDeferred = async {

        runCatching {
            useCase.getCoinList()
        }
    }

    val pokemonDeferred = async {

        runCatching {
            useCase.getPokemonList()
        }
    }

    val coinResult = coinDeferred.await()

    val pokemonResult = pokemonDeferred.await()
}

This is a very common MVI production pattern.

coroutineScope vs supervisorScope

FeaturecoroutineScopesupervisorScopeOne child failsAll cancelOthers surviveStructured concurrencyStrictRelaxedBest forDependent tasksIndependent tasks

5. Cooperative Cancellation

Very important concept.

Coroutines do NOT stop immediately.

They stop only at:

  • delay()
  • await()
  • yield()
  • suspension points

Example:

while(true) {}

This NEVER cancels.

Correct Way

while(isActive) {}

Or:

ensureActive()

Why?

Coroutine cancellation is cooperative.

The coroutine must check whether it is cancelled.

6. CancellationException

Internally, cancellation works using:

CancellationException

And this is VERY important.

Wrong Way

catch (e: Exception) {}

This accidentally catches cancellation too.

Result:

  • coroutine may not cancel properly

Correct Way

catch (e: CancellationException) {
    throw e
}
catch (e: Exception) {}

Why Re-throw?

Because cancellation is NOT a failure.

It is a control signal.

7. async Cancellation

You can cancel individual async tasks.

val deferred = async {

delay(5000)

    "Result"
}

deferred.cancel()

Only this async block cancels.

8. Timeout Cancellation

Very useful for APIs.

withTimeout(3000) {

  repository.getData()

}

If API takes more than 3 seconds:

  • coroutine automatically cancels

Safe Timeout Example

val result = withTimeoutOrNull(3000) {

  repository.getData()

}

Returns:

  • null instead of crash

9. Lifecycle-Aware Cancellation

Android already provides automatic cancellation.

viewModelScope

Cancels when:

  • ViewModel clears

lifecycleScope

Cancels when:

  • Lifecycle destroyed

Example:

viewModelScope.launch {}

No need to manually cancel on screen destroy.

Kotlin handles it automatically.

Production-Level Example

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val repository: Repository
) : ViewModel() {

private var loadJob: Job? = null
    fun loadData() {

        loadJob?.cancel()

        loadJob = viewModelScope.launch {

              supervisorScope {

                val api1 = async {
                    repository.getCoins()
                }
                val api2 = async {
                    repository.getPokemon()
                }
                val coins = api1.await()
                val pokemon = api2.await()
            }
        }
    }
}

When Should You Use Cancellation?

ScenarioUse Cancellation?Search APIYesRetry buttonYesPaginationYesFile UploadYesParallel APIsUsuallyOne-time splash APINot necessary

Final Thoughts

Coroutine cancellation is not just:

job.cancel()

It is deeply connected with:

  • structured concurrency
  • lifecycle awareness
  • failure propagation
  • app performance
  • memory management

Understanding cancellation properly will make your Android architecture significantly more production-ready.

Especially in:

  • MVI
  • Clean Architecture
  • Compose apps
  • Parallel APIs
  • Real-time search systems

Mastering cancellation is one of the biggest steps toward becoming an advanced Kotlin developer.


메타데이터
post_id
391a52ad8d8d
slug
understanding-coroutine-cancellation-in-kotlin-complete-guide-with-real-android-examples-391a52ad8d8d
url
https://medium.com/@meaghosh/understanding-coroutine-cancellation-in-kotlin-complete-guide-with-real-android-examples-391a52ad8d8d
canonical_url
https://medium.com/@meaghosh/understanding-coroutine-cancellation-in-kotlin-complete-guide-with-real-android-examples-391a52ad8d8d
author_url
https://medium.com/@meaghosh
status
ok
fetched_at
2026-06-23 03:48:11