Guías prácticas

Consideraciones sobre el rendimiento más detalladas

Lectura de 8 minutos

Tranquilizaos y dejad que os expliquemos más detalles sobre el rendimiento.

Te damos la bienvenida al tercer día de la Semana de la Visibilidad del Rendimiento. Hoy vamos a seguir compartiendo detalles y directrices sobre aspectos importantes del rendimiento de las aplicaciones. Hablaremos sobre la optimización guiada por perfil, las mejoras de rendimiento de Jetpack Compose y las consideraciones sobre el trabajo detrás de las cámaras. Vamos a entrar en materia.

Optimización guiada por perfil

Los perfiles de base y los perfiles de inicio son fundamentales para mejorar el rendimiento de inicio y de tiempo de ejecución de una aplicación Android. Forman parte de un grupo de optimizaciones del rendimiento llamado Optimización guiada por perfil.

Cuando se empaqueta una aplicación, el dexer d8 toma clases y métodos y rellena los archivos classes.dex de la aplicación. Cuando un usuario abre la aplicación, estos archivos dex se cargan uno tras otro hasta que la aplicación puede iniciarse. Si proporcionas un perfil de inicio, indicas a d8 qué clases y métodos debe incluir en los primeros archivos classes.dex. Esta estructura permite que la aplicación cargue menos archivos, lo que a su vez mejora la velocidad de inicio.

Los perfiles de línea de base trasladan de forma eficaz los pasos de compilación Just-in-Time (JIT) de los dispositivos de los usuarios a los ordenadores de los desarrolladores. Se ha demostrado que el código compilado con la compilación anticipada (AOT) reduce el tiempo de inicio y los problemas de renderización.

Trello y los perfiles de referencia

Hemos preguntado a los ingenieros de la aplicación Trello cómo han afectado los perfiles base al rendimiento de su aplicación. Tras aplicar Baseline Profiles a su recorrido de usuario principal, Trello consiguió reducir significativamente el tiempo de inicio de la aplicación en un 25 %.

imagen.png

Trello consiguió mejorar el tiempo de inicio de su aplicación en un 25 % usando perfiles de referencia.

Perfiles de Baseline en Meta

Además, los ingenieros de Meta han publicado recientemente un artículo sobre cómo están acelerando sus aplicaciones Android con Baseline Profiles.

imagen.png

En las aplicaciones de Meta, los equipos han observado que varias métricas críticas han mejorado hasta un 40 % después de aplicar los perfiles de línea de base.

Las mejoras técnicas como estas también te ayudan a aumentar la satisfacción de los usuarios y el éxito de tu empresa. Compartir esta información con los propietarios de tus productos, los directores de tecnología y las personas que toman decisiones también puede ayudar a acelerar el rendimiento de tu aplicación.

Empezar a usar los perfiles de referencia

Para generar un perfil de línea de base o de inicio, escribe una prueba de macrobenchmark que ponga a prueba la aplicación. Durante la prueba, se recogen datos de perfil que se usarán durante la compilación de la aplicación. Las pruebas se escriben con la nueva API UiAutomator, que veremos mañana.

Escribir una prueba comparativa como esta es sencillo. Puedes ver el ejemplo completo en GitHub.

  @Test

fun profileGenerator() {

    rule.collect(

        packageName = TARGET_PACKAGE,

        maxIterations = 15,

        stableIterations = 3,

        includeInStartupProfile = true

    ) {

        uiAutomator {

            startApp(TARGET_PACKAGE)

        }

    }


}

Consideraciones

Empieza escribiendo un perfil de Baseline de pruebas de macrobenchmark y un perfil de inicio para la ruta más habitual de tus usuarios. Es decir, el punto de entrada principal que usan los usuarios para acceder a tu aplicación, que suele ser después de iniciar sesión. A continuación, escribe más casos de prueba para obtener una imagen más completa solo de los perfiles de referencia. No es necesario que cubras todo con un perfil de Baseline. Sigue las rutas más usadas y mide el rendimiento en el campo. Hablaremos más sobre este tema en la publicación de mañana.

Empezar a usar la optimización guiada por el perfil

Para saber cómo funcionan los perfiles base, consulta este vídeo del Android Developers Summit:

También puedes ver el episodio de Android Build Time sobre la optimización guiada por perfil para obtener más información: 

También tenemos guías completas sobre perfiles de línea de base y perfiles de inicio que puedes consultar.

Mejoras de rendimiento de Jetpack Compose

El equipo de ingeniería ha invertido en el rendimiento del framework de la interfaz de usuario de Android, y los resultados han sido muy positivos. Desde la versión 1.9 de Jetpack Compose, el jank de desplazamiento se ha reducido al 0,2 % durante una prueba interna de larga duración. 

jankyFrames.png

Estas mejoras han sido posibles gracias a varias funciones incluidas en las versiones más recientes.

Ventana de caché personalizable

De forma predeterminada, los diseños diferidos solo componen un elemento con antelación en la dirección del desplazamiento y, cuando un elemento se desplaza fuera de la pantalla, se descarta. Ahora puedes personalizar la cantidad de elementos que se conservarán mediante una fracción del tamaño del viewport o del tamaño en dp. De esta forma, tu aplicación puede realizar más trabajo por adelantado y, después de habilitar la composición pausable entre fotogramas, usar el tiempo disponible de forma más eficiente.

Para empezar a usar ventanas de caché personalizables, crea una instancia de LazyLayoutCacheWindow y pásala a tu lista o cuadrícula lazy. Mide el rendimiento de tu aplicación con diferentes tamaños de ventana de caché, por ejemplo, el 50% del viewport. El valor óptimo dependerá de la estructura de tu contenido y del tamaño de los elementos.

  val dpCacheWindow = LazyLayoutCacheWindow(ahead = 150.dp, behind = 100.dp)

val state = rememberLazyListState(cacheWindow = dpCacheWindow)

LazyColumn(state = state) {

    // column contents

}

Composición que se puede pausar

Esta función permite pausar las composiciones y dividir su trabajo en varios fotogramas. Las APIs se lanzaron en la versión 1.9 y ahora se usan de forma predeterminada en la versión 1.10 en la precarga de diseño diferido. Notarás más ventajas con los elementos complejos que tengan tiempos de composición más largos. 

imagen.png

Más optimizaciones del rendimiento de Compose

En las versiones 1.9 y 1.10 de Compose, el equipo también ha hecho varias optimizaciones que son un poco menos obvias.

Se han mejorado varias APIs que usan corrutinas en segundo plano. Por ejemplo, al usar Draggable y Clickable, los desarrolladores deberían observar tiempos de reacción más rápidos y un mayor número de asignaciones.

Las optimizaciones en el seguimiento de rectángulos de diseño han mejorado el rendimiento de modificadores como onVisibilityChanged() y onLayoutRectChanged(). De esta forma, se acelera la fase de diseño, incluso cuando no se usan estas APIs de forma explícita.

Otra mejora del rendimiento es el uso de valores almacenados en caché al observar las posiciones mediante onPlaced().

Prefetch text in the background

A partir de la versión 1.9, Compose añade la capacidad de prefetch de texto en un subproceso en segundo plano. Esto te permite precalentar las cachés para que el diseño del texto sea más rápido, lo que es importante para el rendimiento de la renderización de la aplicación. Durante la maquetación, el texto se debe transferir al framework de Android, donde se rellena una caché de palabras. De forma predeterminada, se ejecuta en el hilo de la interfaz de usuario. Delegar la prefetización y rellenar la caché de palabras en un subproceso en segundo plano puede acelerar el diseño, sobre todo en el caso de textos más largos. Para prefetch en un subproceso en segundo plano, puedes pasar un ejecutor personalizado a cualquier elemento componible que use BasicText en segundo plano pasando un LocalBackgroundTextMeasurementExecutor a un CompositionLocalProvider de esta forma.

  val defaultTextMeasurementExecutor = Executors.newSingleThreadExecutor()

CompositionLocalProvider(

    LocalBackgroundTextMeasurementExecutor provides DefaultTextMeasurementExecutor

) {

    BasicText("Some text that should be measured on a background thread!")


}

En función del texto, esto puede mejorar el rendimiento de la renderización del texto. Para asegurarte de que mejora el rendimiento de renderizado de tu aplicación, haz una prueba comparativa y compara los resultados.

Consideraciones sobre el rendimiento del trabajo en segundo plano

El trabajo en segundo plano es una parte esencial de muchas aplicaciones. Es posible que uses bibliotecas como WorkManager o JobScheduler para realizar tareas como las siguientes:

  • Subir eventos analíticos periódicamente
  • Sincronizar datos entre un servicio de backend y una base de datos
  • Procesar contenido multimedia (por ejemplo, cambiar el tamaño o comprimir imágenes)

Uno de los principales retos a la hora de llevar a cabo estas tareas es encontrar el equilibrio entre el rendimiento y la eficiencia energética. WorkManager te permite conseguir este equilibrio. Está diseñada para ser eficiente y permitir que el trabajo se posponga hasta una ventana de ejecución óptima influida por varios factores, incluidas las restricciones que especifiques o las que imponga el sistema. 

Sin embargo, WorkManager no es una solución universal. Android también tiene varias APIs optimizadas para el consumo de energía que se han diseñado específicamente para determinados recorridos de usuario principales (CUJs) habituales.  

Consulta la página de destino de las tareas en segundo plano para ver una lista de algunas de ellas,  como actualizar un widget y obtener la ubicación en segundo plano.

Herramientas de depuración local para el trabajo en segundo plano: casos habituales

Para depurar el trabajo en segundo plano y saber por qué se ha retrasado o no se ha podido completar una tarea, debes tener visibilidad de cómo ha programado el sistema tus tareas. 

Para ayudarte con esto, WorkManager tiene varias herramientas relacionadas para depurar localmente y optimizar el rendimiento (algunas de ellas también funcionan con JobScheduler). A continuación, se indican algunos casos habituales que pueden surgir al usar WorkManager y se explican las herramientas que puedes usar para depurarlos.

Depurar por qué no se ejecuta el trabajo programado

El retraso o la falta de ejecución de un trabajo programado puede deberse a varios factores, como el incumplimiento de las restricciones especificadas o las restricciones impuestas por el sistema

El primer paso para investigar por qué no se ejecuta el trabajo programado es confirmar que se ha programado correctamente. Después de confirmar el estado de la programación, determina si hay alguna restricción o condición previa que impida que se ejecute el trabajo.

Hay varias herramientas para depurar este caso.

Inspector de tareas en segundo plano

El Inspector de tareas en segundo plano es una potente herramienta integrada directamente en Android Studio. Proporciona una representación visual de todas las tareas de WorkManager y sus estados asociados (Running, Enqueued, Failed y Succeeded). 

Para depurar por qué no se ejecuta el trabajo programado con el inspector de tareas en segundo plano, consulta los estados de trabajo que se indican. El estado "En cola" indica que tu trabajo se ha programado, pero aún está esperando a ejecutarse.

Ventajas: además de ofrecer una forma sencilla de ver todas las tareas, esta herramienta es especialmente útil si tienes trabajo encadenado. El inspector de tareas en segundo plano ofrece un gráfico que puede visualizar si un error en una tarea anterior ha afectado a la ejecución de la siguiente.

imagen.png

Vista de lista del Inspector de tareas en segundo plano

imagen.png

Vista de gráfico del Inspector de tareas en segundo plano

adb shell dumpsys jobscheduler

Este comando devuelve una lista de todos los trabajos de JobScheduler activos (que incluye los elementos Worker de WorkManager) junto con las restricciones especificadas y las impuestas por el sistema. También devuelve el historial de tareas. 

Usa esta opción si quieres ver el trabajo programado y las restricciones asociadas de otra forma. En las versiones de WorkManager anteriores a la 2.10.0, adb shell dumpsys jobscheduler devolverá una lista de Workers con este nombre:

  [package name]/androidx.work.impl.background.systemjob.SystemJobService

Si tu aplicación tiene varios elementos Worker, al actualizar a WorkManager 2.10.0 podrás ver los nombres de los elementos Worker y distinguirlos fácilmente:

  #WorkerName#@[package name]/androidx.work.impl.background.systemjob.SystemJobService

Ventajas:  este comando es útil para saber si ha habido alguna restricción impuesta por el sistema , que no se puede determinar con el inspector de tareas en segundo plano. Por ejemplo, esto devolverá el contenedor de espera de tu aplicación, que puede afectar a la ventana en la que se completa el trabajo programado.

Habilitar registro de depuración

Puedes habilitar el registro personalizado para ver registros detallados de WorkManager, que tendrán WM— adjunto. 

Ventajas: te permite ver cuándo se programa el trabajo, cuándo se cumplen las restricciones y los eventos del ciclo de vida. Además, puedes consultar estos registros mientras desarrollas tu aplicación.

WorkInfo.StopReason

Si observas un rendimiento impredecible con un trabajador específico, puedes observar mediante programación el motivo por el que se detuvo en el intento anterior con WorkInfo.getStopReason

Es recomendable configurar tu aplicación para que observe WorkInfo mediante getWorkInfoByIdFlow y así identificar si tu trabajo se ve afectado por restricciones en segundo plano, restricciones, tiempos de espera frecuentes o si el usuario lo ha detenido.

Ventajas: puedes usar WorkInfo.StopReason para recoger datos de campo sobre el rendimiento de tus trabajadores.

Depurar la duración alta de wake lock atribuida a WorkManager marcada por Android vitals

Android vitals incluye la métrica de wake locks parciales excesivos, que destaca los wake locks que contribuyen al consumo de batería. Puede que te sorprenda saber que WorkManager adquiere bloqueos de activación para ejecutar tareas y que, si los bloqueos de activación superan el umbral establecido por Google Play, pueden afectar a la visibilidad de tu aplicación. ¿Cómo puedes depurar por qué se atribuye tanta duración de wake lock a tu trabajo? Puedes usar las siguientes herramientas.

Panel de control de Android vitals

Primero, confirma en el panel de control de Android Vitals sobre el bloqueo de activación excesivo que la duración elevada del bloqueo de activación procede de WorkManager y no de una alarma u otro bloqueo de activación. Puedes consultar la documentación Identificar los bloqueos de activación creados por otras APIs para saber qué bloqueos de activación se mantienen debido a WorkManager. 

Perfetto

Perfetto es una herramienta para analizar las trazas del sistema. Si la usas para depurar WorkManager específicamente, puedes consultar la sección "Estado del dispositivo" para ver cuándo ha empezado tu trabajo, cuánto tiempo ha durado y cómo contribuye al consumo de energía. 

En "Estado del dispositivo: tareas", puedes ver los workers que se han ejecutado y sus bloqueos de activación asociados.

deviceState.png

Sección Estado del dispositivo de Perfetto, que muestra la ejecución de CleanupWorker y BlurWorker.

Recursos

Consulta la página de depuración de WorkManager para ver una descripción general de los métodos de depuración disponibles para otros casos que puedas encontrar.

Para probar algunos de estos métodos y obtener más información sobre la depuración de WorkManager, consulta el codelab Advanced WorkManager and Testing.

Pasos siguientes

Hoy hemos ido más allá de la reducción de código y hemos analizado cómo renderizan tu aplicación Android Runtime y Jetpack Compose. Tanto si se trata de precompilar rutas críticas con Baseline Profiles como de suavizar los estados de desplazamiento con las nuevas funciones de Compose 1.9 y 1.10, estas herramientas se centran en la sensación de tu aplicación. Además, hemos profundizado en las prácticas recomendadas para depurar el trabajo en segundo plano.

Pregunta a Android

El viernes organizaremos una sesión de preguntas y respuestas en directo sobre el rendimiento. Envía tus preguntas ahora con el hashtag #AskAndroid y los expertos te responderán.

El reto

El lunes te propusimos que habilitaras R8. Hoy te pedimos que generes un perfil de Baseline para tu aplicación.

Con Android Studio Otter, el asistente del módulo Generador de perfiles de Baseline hace que sea más fácil que nunca. Elige el recorrido de usuario crítico más importante (aunque solo sea el inicio y el inicio de sesión de tu aplicación) y genera un perfil.

Una vez que lo tengas, ejecuta una macroprueba comparativa para comparar CompilationMode.None con CompilationMode.Partial.

Comparte las mejoras que has conseguido en el tiempo de inicio en las redes sociales con el hashtag #optimizationEnabled.

No te lo pierdas mañana

Has reducido el tamaño de tu aplicación con R8 y has optimizado su tiempo de ejecución con la optimización guiada por perfil. Pero ¿cómo demuestras estos logros a las partes interesadas? ¿Y cómo detectáis las regresiones antes de que afecten a la producción?

Únete mañana al día 4: Guía para nivelar el rendimiento, donde te explicaremos cómo medir tu éxito, desde los datos de campo de Estadísticas de Play hasta el seguimiento local detallado con Perfetto.

Escrito por:

Seguir leyendo