Diagnostica problemas de estabilidad

Si tienes problemas de rendimiento que se deben a una recomposición innecesaria o excesiva, debes depurar la estabilidad de tu app. En esta guía, se describen varios métodos para hacerlo.

Inspector de diseño

El Inspector de diseño de Android Studio te permite ver qué elementos componibles se vuelven a componer en tu app. Muestra los recuentos de cuántas veces Compose volvió a componer o omitió un componente.

Recuentos de recomposición y omisiones en el Inspector de diseño

Informes del compilador de Compose

El compilador de Compose puede generar los resultados de su inferencia de estabilidad para su inspección. Con este resultado, puedes determinar cuáles de tus funciones de componibilidad se pueden omitir y cuáles no. En las siguientes subsecciones, se resume cómo usar estos informes, pero para obtener información más detallada, consulta la documentación técnica.

Configuración

Los informes del compilador de Compose no están habilitados de forma predeterminada. Puedes activarlas con una marca del compilador. La configuración exacta varía según tu proyecto, pero, para los proyectos que usan el complemento de Gradle del compilador de Compose, puedes agregar lo siguiente en el archivo build.gradle de cada módulo.

  android { ... }

  composeCompiler {
    reportsDestination = layout.buildDirectory.dir("compose_compiler")
    metricsDestination = layout.buildDirectory.dir("compose_compiler")
  }

Ahora, se generarán informes del compilador de Compose cuando compiles tu proyecto.

Resultado de ejemplo

El reportsDestination genera tres archivos. Los siguientes son resultados de ejemplo de JetSnack.

  • <modulename>-classes.txt: Es un informe sobre la estabilidad de las clases en este módulo. Muestra.
  • <modulename>-composables.txt: Es un informe sobre cómo se pueden reiniciar y omitir las funciones de componibilidad del módulo. Muestra.
  • <modulename>-composables.csv: Es una versión CSV del informe de elementos de componibilidad que puedes importar a una hoja de cálculo o procesar con una secuencia de comandos. Muestra

Informe de elementos componibles

El archivo composables.txt detalla cada función de componibilidad del módulo determinado, incluida la estabilidad de sus parámetros y si se pueden reiniciar o omitir. A continuación, se muestra un ejemplo hipotético de JetSnack:

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun SnackCollection(
  stable snackCollection: SnackCollection
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
  stable index: Int = @static 0
  stable highlight: Boolean = @static true
)

En este ejemplo, cada parámetro de SnackCollection se marca como stable, por lo que Compose compara cada parámetro con la igualdad estructural (Object.equals()). Si bien los parámetros stable son ideales para valores primitivos y clases de datos pequeñas y planas con verificaciones .equals() económicas, forzar que los modelos que contienen colecciones (como un SnackCollection que une un List<Snack>) sean stable hace que Compose llame a .equals() en cada elemento en cada recomposición. Con el modo de omisión avanzada habilitado, es preferible mantener los modelos que contienen colecciones unstable para que Compose los compare con la igualdad de instancias (===).

Como alternativa, considera otro ejemplo generado con el modo de omisión avanzada inhabilitado:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  stable index: Int
  unstable snacks: List<Snack>
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
)

Cuando el modo de omisión avanzada está inhabilitado (antes de Kotlin 2.0.20), el elemento componible HighlightedSnacks no se puede omitir. Compose nunca lo omite durante la recomposición, incluso si ninguno de sus parámetros cambió, debido al parámetro unstable snacks.

Con el modo de omisión avanzada habilitado (el valor predeterminado en Kotlin 2.0.20 y versiones posteriores), HighlightedSnacks se marca como skippable, y Compose compara el parámetro unstable snacks con la igualdad de instancias (===). Como resultado, Compose omite HighlightedSnacks si se pasa la misma referencia de List y solo la recompone si se pasa una instancia de List recién asignada.

Informe de clases

El archivo classes.txt contiene un informe similar sobre las clases del módulo determinado. El siguiente fragmento es el resultado de la clase Snack:

unstable class Snack {
  stable val id: Long
  stable val name: String
  stable val imageUrl: String
  stable val price: Long
  stable val tagline: String
  unstable val tags: Set<String>
  <runtime stability> = Unstable
}

Como referencia, la siguiente es la definición de Snack:

data class Snack(
    val id: Long,
    val name: String,
    val imageUrl: String,
    val price: Long,
    val tagline: String = "",
    val tags: Set<String> = emptySet()
)

El compilador de Compose marcó Snack como inestable. Esto se debe a que el tipo del parámetro tags es Set<String>. Este es un tipo inmutable, dado que no es un MutableSet. Sin embargo, las clases de colección estándar, como Set, List y Map, son, en última instancia, interfaces. Por lo tanto, la implementación subyacente puede seguir siendo mutable.

Por ejemplo, podrías escribir val set: Set<String> = mutableSetOf("foo"). La variable es constante y su tipo declarado no es mutable, pero su implementación sí lo es. El compilador de Compose no puede estar seguro de la inmutabilidad de esta clase, ya que solo ve el tipo declarado. Por lo tanto, marca tags como inestable.