Recomposition Counts watched catalog rows skip recomposition. Compose 234 may skip a composable when all its parameters are equal to last time, and it trusts equals() only for stable types: ones whose public properties are immutable or observable (State), such as primitives, String, function types and data classes made of them. The compiler plugin writes its verdicts to files when asked:
composeCompiler { // 5.36.4 what the compiler decided about stability
reportsDestination = layout.buildDirectory.dir("compose_compiler")
metricsDestination = layout.buildDirectory.dir("compose_compiler")
}The next build wrote app-classes.txt, app-composables.txt and a JSON metrics file; from the first two:
stable class com.example.booknest.data.Book {
stable val id: Int
stable val title: String
...
<runtime stability> = Stable
}
restartable skippable scheme("[androidx.compose.ui.UiComposable]")
fun com.example.booknest.ui.BookCard(
book: Book
stable modifier: Modifier? = @static <expression>
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]")
fun com.example.booknest.camera.CameraScreen(
stable modifier: Modifier? = @static <expression>
unstable vm: CameraViewModel? = @dynamic <expression>
)Book, all vals of stable types, is inferred stable. CameraViewModel is unstable: it holds CameraX 234 use cases, mutable Java objects the compiler can't reason about. Yet CameraScreen is still skippable, and the metrics file says why: "StrongSkipping": true. Strong skipping, on by default since Kotlin 2.0.20, makes every restartable composable skippable, comparing unstable arguments by instance (===) and stable ones with equals(), and it remembers lambdas automatically. BookNest's release build reported 187 composables, 29 of its 60 classes stable and 21 unstable.
Strong skipping moves the problem rather than removing it: an unstable argument that is a new instance with equal content still forces recomposition. The classic case is a list rebuilt on every emission, such as a Room 234 query that allocates fresh List<Book> objects. The fixes, in order of preference:
Pass stable, minimal data: book.title instead of the whole ViewModel.
Use immutable collections: ImmutableList from kotlinx.collections.immutable (https://github.com/Kotlin/kotlinx.collections.immutable 1,421 ) is stable.
Annotate your own class @Immutable (never changes after construction) or @Stable (changes are observable through State); this is a promise the compiler doesn't check.
Declare classes you don't own stable in a stability configuration file (stabilityConfigurationFiles in composeCompiler), for example java.time.*.