Kotlin Concurrency Expert

new-silvermoon/awesome-android-agent-skills/.github/skills/concurrency_and_networking/kotlin-concurrency-expert

by new-silvermoon82900eacc8dbe13de93c6310af27b9df4b2bd2f6No licenseListed Oct 9, 2026Updated Oct 9, 2026

Kotlin Coroutines review and remediation for Android. Use when asked to review concurrency usage, fix coroutine-related bugs, improve thread safety, or resolve lifecycle issues in Kotlin/Android code.

Instructions onlySoftware Development
AI-generated overview

Reviews and fixes Kotlin Coroutines concurrency issues in Android code, covering scopes, lifecycle safety and thread safety.

What it does
Guides an agent through triaging coroutine problems in Android codebases, such as ANRs, memory leaks, race conditions and incorrect state, then applying the smallest safe fix. It covers dispatcher injection, lifecycle-aware flow collection, state encapsulation, cancellation handling, callback-to-flow conversion and scope selection. It also provides a testing pattern using runTest and test dispatchers. The deliverable is review guidance and corrected code edits rather than generated files.
When to use it
Use when asked to review concurrency usage, fix coroutine-related bugs, improve thread safety, or resolve lifecycle issues in Kotlin or Android code. It fits code review and remediation work on existing coroutine code.
Requirements
No scripts or bundled assets; instructions only. The reviewed project is expected to use Kotlin Coroutines for Android, with kotlinx-coroutines-android and lifecycle-runtime-ktx, and testing patterns reference kotlinx-coroutines-test.

Kotlin Concurrency Expert

Overview

Review and fix Kotlin Coroutines issues in Android codebases by applying structured concurrency, lifecycle safety, proper scoping, and modern best practices with minimal behavior changes.

Workflow

1. Triage the Issue

  • Capture the exact error, crash, or symptom (ANR, memory leak, race condition, incorrect state).
  • Check project coroutines setup: kotlinx-coroutines-android version, lifecycle-runtime-ktx version.
  • Identify the current scope context (viewModelScope, lifecycleScope, custom scope, or none).
  • Confirm whether the code is UI-bound (Dispatchers.Main) or intended to run off the main thread (Dispatchers.IO, Dispatchers.Default).
  • Verify Dispatcher injection patterns for testability.

2. Apply the Smallest Safe Fix

Prefer edits that preserve existing behavior while satisfying structured concurrency and lifecycle safety.

Common fixes:

  • ANR / Main thread blocking: Move heavy work to withContext(Dispatchers.IO) or Dispatchers.Default; ensure suspend functions are main-safe.
  • Memory leaks / zombie coroutines: Replace GlobalScope with a lifecycle-bound scope (viewModelScope, lifecycleScope, or injected applicationScope).
  • Lifecycle collection issues: Replace deprecated launchWhenStarted with repeatOnLifecycle(Lifecycle.State.STARTED).
  • State exposure: Encapsulate MutableStateFlow / MutableSharedFlow; expose read-only StateFlow or Flow.
  • CancellationException swallowing: Ensure generic catch (e: Exception) blocks rethrow CancellationException.
  • Non-cooperative cancellation: Add ensureActive() or yield() in tight loops for cooperative cancellation.
  • Callback APIs: Convert listeners to callbackFlow with proper awaitClose cleanup.
  • Hardcoded Dispatchers: Inject CoroutineDispatcher via constructor for testability.

Critical Rules

Dispatcher Injection (Testability)

kotlin
// CORRECT: Inject dispatcherclass UserRepository(    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO) {    suspend fun fetchUser() = withContext(ioDispatcher) { ... }}
// INCORRECT: Hardcoded dispatcherclass UserRepository {    suspend fun fetchUser() = withContext(Dispatchers.IO) { ... }}

Lifecycle-Aware Collection

kotlin
// CORRECT: Use repeatOnLifecycleviewLifecycleOwner.lifecycleScope.launch {    viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {        viewModel.uiState.collect { state -> updateUI(state) }    }}
// INCORRECT: Direct collection (unsafe, deprecated)lifecycleScope.launchWhenStarted {    viewModel.uiState.collect { state -> updateUI(state) }}

State Encapsulation

kotlin
// CORRECT: Expose read-only StateFlowclass MyViewModel : ViewModel() {    private val _uiState = MutableStateFlow(UiState())    val uiState: StateFlow<UiState> = _uiState.asStateFlow()}
// INCORRECT: Exposed mutable stateclass MyViewModel : ViewModel() {    val uiState = MutableStateFlow(UiState()) // Leaks mutability}

Exception Handling

kotlin
// CORRECT: Rethrow CancellationExceptiontry {    doSuspendWork()} catch (e: CancellationException) {    throw e // Must rethrow!} catch (e: Exception) {    handleError(e)}
// INCORRECT: Swallows cancellationtry {    doSuspendWork()} catch (e: Exception) {    handleError(e) // CancellationException swallowed!}

Cooperative Cancellation

kotlin
// CORRECT: Check for cancellation in tight loopssuspend fun processLargeList(items: List<Item>) {    items.forEach { item ->        ensureActive() // Check cancellation        processItem(item)    }}
// INCORRECT: Non-cooperative (ignores cancellation)suspend fun processLargeList(items: List<Item>) {    items.forEach { item ->        processItem(item) // Never checks cancellation    }}

Callback Conversion

kotlin
// CORRECT: callbackFlow with awaitClosefun locationUpdates(): Flow<Location> = callbackFlow {    val listener = LocationListener { location ->        trySend(location)    }    locationManager.requestLocationUpdates(listener)        awaitClose { locationManager.removeUpdates(listener) }}

Scope Guidelines

ScopeUse WhenLifecycle
viewModelScopeViewModel operationsCleared with ViewModel
lifecycleScopeUI operations in Activity/FragmentDestroyed with lifecycle owner
repeatOnLifecycleFlow collection in UIStarted/Stopped with lifecycle state
applicationScope (injected)App-wide background workApplication lifetime
GlobalScopeNEVER USEBreaks structured concurrency

Testing Pattern

kotlin
@Testfun `loading data updates state`() = runTest {    val testDispatcher = StandardTestDispatcher(testScheduler)    val repository = FakeRepository()    val viewModel = MyViewModel(repository, testDispatcher)        viewModel.loadData()    advanceUntilIdle()        assertEquals(UiState.Success(data), viewModel.uiState.value)}

Reference Material

Source and attribution

Source:new-silvermoon/awesome-android-agent-skillsin.github/skills/concurrency_and_networking/kotlin-concurrency-expertat commit82900ea

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal