Diagnostica problemas de estabilidad

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

Layout Inspector

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 veces que Compose recomponió 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 la inspección. Con este resultado, puedes determinar cuáles de tus elementos componibles 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 no están habilitados de forma predeterminada. Puedes activarlos con una marca del compilador. La configuración exacta varía según el proyecto, pero, para la mayoría de los proyectos, puedes pegar la siguiente secuencia de comandos en el archivo raíz build.gradle.

Groovy

subprojects {
  tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
        kotlinOptions {
            if (project.findProperty("composeCompilerReports") == "true") {
                freeCompilerArgs += [
                        "-P",
                        "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
                                project.buildDir.absolutePath + "/compose_compiler"
                ]
            }
            if (project.findProperty("composeCompilerMetrics") == "true") {
                freeCompilerArgs += [
                        "-P",
                        "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
                                project.buildDir.absolutePath + "/compose_compiler"
                ]
            }
        }
    }
}

Kotlin

subprojects {
    tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
        kotlinOptions {
            if (project.findProperty("composeCompilerReports") == "true") {
                freeCompilerArgs += listOf(
                    "-P",
                    "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${project.buildDir.absolutePath}/compose_compiler"
                )
            }
            if (project.findProperty("composeCompilerMetrics") == "true") {
                freeCompilerArgs += listOf(
                    "-P",
                    "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=${project.buildDir.absolutePath}/compose_compiler"
                )
            }
        }
    }
}

Ejecuta la tarea

Para depurar la estabilidad de tus elementos componibles, ejecuta la tarea de la siguiente manera:

./gradlew assembleRelease -PcomposeCompilerReports=true

Ejemplo de resultado:

Esta tarea genera tres archivos. Los siguientes son ejemplos de resultados de JetSnack.

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

Informe de elementos componibles

En el archivo composables.txt, se detalla cada función de componibilidad para el módulo determinado, incluida la estabilidad de sus parámetros y si se pueden omitir o se pueden reiniciar. El siguiente es 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
)

Este elemento SnackCollection componible es completamente reiniciable, se puede omitir y es estable. Esto suele ser preferible, aunque no ciertamente no es obligatorio.

Por otro lado, veamos otro ejemplo.

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
)

No se puede omitir el elemento HighlightedSnacks componible. Compose nunca la omite durante la recomposición. Esto ocurre incluso si no cambió ninguno de sus parámetros. Esto se debe al parámetro unstable, snacks.

Informe de clases

El archivo classes.txt contiene un informe similar sobre las clases del módulo dado. 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
}

A modo de referencia, esta 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 interfaces. Por lo tanto, la implementación subyacente puede seguir siendo mutable.

Por ejemplo, puedes escribir val set: Set<String> = mutableSetOf("foo"). La variable es constante y el tipo declarado no es mutable, pero su implementación aún es mutable. 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.