Back to skills
extension
Category: Development & EngineeringNo API key required

android-compose-performance

Optimize Android Jetpack Compose and Kotlin coroutine performance. Use when reviewing, writing, or refactoring Android Compose UI code for performance issues—recomposition problems, unstable types, lazy layout inefficiencies, improper state management, or coroutine misuse. Also use when the user asks about Compose recomposition, state hoisting, stability annotations, dispatcher selection, Flow/Channel patterns, coroutine synchronization, or Android IPC mechanisms (Messenger, AIDL, Binder).

personAuthor: jakexiaohubgithub

Android Compose & Kotlin Performance

Workflow

  1. Identify the problem area:

  2. Apply targeted fixes using the patterns in the relevant reference file.

  3. Verify in release mode — debug builds disable Compose compiler optimizations and skippability, making profiling unreliable.

Quick Reference: Most Common Mistakes

Compose

  • Reading state in parent when only child needs it — pass () -> T lambdas instead
  • Using List/Map/Set params (unstable) — use kotlinx.collections.immutable or @Immutable
  • Missing key in LazyColumn/LazyRow items
  • Using Modifier.offset(x, y) instead of Modifier.offset { IntOffset(x, y) } for animated values
  • Sorting/filtering inside composables instead of ViewModel
  • Profiling in debug builds

Coroutines

  • Using GlobalScope instead of viewModelScope/lifecycleScope
  • Using synchronized/ReentrantLock instead of Mutex.withLock in coroutine code
  • Collecting StateFlow without repeatOnLifecycle or collectAsStateWithLifecycle()
  • Using Channel when SharedFlow is appropriate (one-to-many) or vice versa
  • Running IO work on Dispatchers.Default or Dispatchers.Main

Recommended Architecture

  • ViewModel: MutableStateFlow for UI state, exposed as StateFlow. Logic in viewModelScope.
  • Repository/data layer: withContext(Dispatchers.IO) for disk/network. Expose Flow.
  • Compose UI: Collect via collectAsStateWithLifecycle(). Defer reads via lambdas. Use stable types. Provide keys to lazy layouts.
  • Cross-coroutine: Channel for one-to-one delivery, SharedFlow for one-to-many broadcast.
  • Synchronization: Mutex.withLock over synchronized in coroutine code.