← All shorts
32Android Studio Giraffe+2 min read

Finding Needless Recomposition with the Layout Inspector

Turn on recomposition counts in Android Studio to see exactly which composables are re-running, and why.

A composable can pass review and still recompose far more often than it needs to, because a diff records what the code says and never once records how many times any of it ran.

Recomposition and skip counts in the Layout Inspector turn that suspicion into a number.

#The old way

@Composable
fun ProfileScreen(viewModel: ProfileViewModel) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()

    Column {
        Header(username = uiState.username)
        FollowButton(
            isFollowing = uiState.isFollowing,
            onClick = { viewModel.toggleFollow(uiState.userId) }
        )
    }
}

Enable "Show Recomposition Counts" in the Layout Inspector, then watch FollowButton. Its count climbs on every ProfileScreen recomposition, for any reason at all, not only when isFollowing flips. Blame the onClick lambda: it captures uiState, so each recomposition allocates a fresh instance and hands it down, and the parameter comparison sees a changed lambda reference where it needed an identical one. No skip.

#The new way

@Composable
fun ProfileScreen(viewModel: ProfileViewModel) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    val userId = uiState.userId

    Column {
        Header(username = uiState.username)
        FollowButton(
            isFollowing = uiState.isFollowing,
            onClick = remember(userId) { { viewModel.toggleFollow(userId) } }
        )
    }
}

Hoist userId out of the state object. Wrap the lambda in remember(userId).

#Why it matters

  • Recompose and skip columns confirm a suspicion in seconds, where reading code produces only a guess. High count, zero skips? Look there first.
  • remember(userId) gives the lambda a stable identity across every recomposition in which userId hasn't changed, so FollowButton's parameter comparison succeeds and it skips.
  • One unstable lambda deep in a list or grid multiplies into hundreds of unnecessary recompositions per frame.

#Gotcha

Code review can't see this one.

Correct code, correct UI, waste underneath both of them, and it surfaces nowhere except as a number in the Layout Inspector or in Compose compiler metrics. Never turn those counts on and you never find out that FollowButton was recomposing forty times a second while the list scrolled past it.

androidcomposeperformancetooling

Related shorts