Question #387HardNative Android

How do you optimize Compose performance?

#jetpack-compose#performance#optimization

Answer

Overview

Optimizing Compose involves reducing unnecessary recompositions and improving rendering efficiency.


Optimization Techniques

1. Use Stable/Immutable Types

kotlin
// ❌ Unstable - always recomposes
class User(var name: String)

// ✅ Stable - smart recomposition
@Immutable
data class User(val name: String)

2. remember for Expensive Operations

kotlin
@Composable
fun ExpensiveList(items: List<Item>) {
    val processed = remember(items) {
        items.map { processExpensive(it) }
    }
    LazyColumn {
        items(processed) { item ->
            Text(item.name)
        }
    }
}

3. derivedStateOf

kotlin
@Composable
fun FilteredList(items: List<Item>, query: String) {
    val filtered by remember {
        derivedStateOf {
            items.filter { it.name.contains(query) }
        }
    }
}

4. key() for Lists

kotlin
LazyColumn {
    items(
        items = users,
        key = { user -> user.id } // Stable key
    ) { user ->
        UserCard(user)
    }
}

5. Avoid Lambda Recreations

kotlin
// ❌ Bad - new lambda every recomposition
@Composable
fun BadExample(viewModel: VM) {
    Button(onClick = { viewModel.onClick() }) {
        Text("Click")
    }
}

// ✅ Good
@Composable
fun GoodExample(viewModel: VM) {
    val onClick = remember { { viewModel.onClick() } }
    Button(onClick = onClick) {
        Text("Click")
    }
}

6. Use @Stable Annotation

kotlin
@Stable
class ViewModel {
    var count by mutableStateOf(0)
        private set
    
    fun increment() { count++ }
}

7. Lazy Layouts

kotlin
// ✅ Use LazyColumn instead of Column with scrolling
LazyColumn {
    items(10000) { index ->
        Text("Item $index")
    }
}

8. BoxWithConstraints Sparingly

kotlin
// ❌ Expensive - measures multiple times
BoxWithConstraints {
    // Layout
}

// ✅ Use when really needed
@Composable
fun AdaptiveLayout() {
    BoxWithConstraints {
        if (maxWidth < 600.dp) {
            CompactLayout()
        } else {
            ExpandedLayout()
        }
    }
}

Debugging Tools

Recomposition Highlighter

kotlin
fun Modifier.recomposeHighlighter(): Modifier = composed {
    val color = remember { Random.nextColor() }
    this.background(color)
}

Layout Inspector

  • Android Studio → View → Tool Windows → Layout Inspector
  • Shows recomposition counts

Golden Rule: Measure first, optimize second. Don't over-optimize.