Instructivos

Consideraciones de rendimiento más detalladas

Lectura de 8 min

Prepárense y permítannos guiarlos a través de más información sobre el rendimiento.

Te damos la bienvenida al día 3 de la Semana de Destacados de Rendimiento. Hoy seguimos compartiendo detalles y orientación sobre áreas importantes del rendimiento de las apps. Hablaremos sobre la optimización guiada por perfil, las mejoras de rendimiento de Jetpack Compose y las consideraciones para trabajar en segundo plano. Comencemos.

Optimización guiada por perfil

Los perfiles de Baseline y los perfiles de inicio son fundamentales para mejorar el rendimiento de inicio y de tiempo de ejecución de una app para Android. Forman parte de un grupo de optimizaciones del rendimiento llamadas optimización guiada por perfil.

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

Los perfiles de Baseline trasladan de manera eficaz los pasos de compilación Just in Time (JIT) de los dispositivos de los usuarios a las máquinas de los desarrolladores. Se demostró que el código compilado con anticipación (AOT) generado reduce el tiempo de inicio y los problemas de renderización.

Trello y perfiles de Baseline

Les preguntamos a los ingenieros de la app de Trello cómo los perfiles de Baseline afectaron el rendimiento de la app. Después de aplicar los perfiles de Baseline a su recorrido del usuario principal, Trello observó una reducción significativa del 25 % en el tiempo de inicio de la app.

image.png

Trello pudo mejorar el tiempo de inicio de su app en un 25 % con los perfiles de Baseline.

Perfiles de Baseline en Meta

Además, los ingenieros de Meta publicaron recientemente un artículo sobre cómo aceleran sus apps para Android con perfiles de Baseline.

image.png

En las apps de Meta, los equipos observaron que varias métricas críticas mejoraron hasta en un 40 % después de aplicar los perfiles de Baseline.

Las mejoras técnicas como estas también te ayudan a aumentar la satisfacción del usuario y el éxito de tu empresa. Compartir esta información con los propietarios de productos, los CTO y los responsables de la toma de decisiones también puede ayudar a acelerar el rendimiento de tu app.

Comienza a usar los perfiles de Baseline

Para generar un perfil de Baseline o de inicio, debes escribir una prueba de macrobenchmark que ejercite la app. Durante la prueba, se recopilan datos de perfil que se usarán durante la compilación de la app. Las pruebas se escriben con la nueva API de UiAutomator, que analizaremos mañana.

Escribir una comparativa como esta es sencillo, y puedes ver la muestra completa en GitHub.

  @Test

fun profileGenerator() {

    rule.collect(

        packageName = TARGET_PACKAGE,

        maxIterations = 15,

        stableIterations = 3,

        includeInStartupProfile = true

    ) {

        uiAutomator {

            startApp(TARGET_PACKAGE)

        }

    }


}

Consideraciones

Comienza por escribir un perfil de Baseline de pruebas de macrobenchmark y un perfil de inicio para la ruta que más recorren tus usuarios. Esto significa el punto de entrada principal que toman tus usuarios en tu app, que suele ser después de que acceden. Luego, sigue escribiendo más casos de prueba para obtener una imagen más completa solo para los perfiles de Baseline. No es necesario que un Perfil de Baseline cubra todo. Utiliza las rutas más usadas y mide el rendimiento en el campo. Hablaremos más sobre eso en la publicación de mañana.

Comienza a usar la optimización guiada por perfil

Para obtener información sobre cómo funcionan los perfiles de Baseline de forma interna, mira este video de la Cumbre de desarrolladores de Android:

Además, mira el episodio de Android Build Time sobre la optimización guiada por el perfil para obtener otra perspectiva detallada: 

También tenemos una guía detallada sobre los perfiles de Baseline y los perfiles de inicio disponibles para que sigas leyendo.

Mejoras en el rendimiento de Jetpack Compose

La inversión en rendimiento del equipo de ingeniería en el framework de IU para Android ha dado sus frutos. Desde la versión 1.9 de Jetpack Compose, el jank de desplazamiento se redujo al 0.2 % durante una prueba comparativa interna de desplazamiento largo. 

jankyFrames.png

Estas mejoras fueron 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 anticipación en la dirección del desplazamiento y, después de que algo se desplaza fuera de la pantalla, se descarta. Ahora puedes personalizar la cantidad de elementos que se conservarán a través de una fracción del tamaño del viewport o de dp. Esto ayuda a tu app a realizar más trabajo por adelantado y, después de habilitar la composición pausable entre fotogramas, a usar el tiempo disponible de manera más eficiente.

Para comenzar a usar ventanas de caché personalizables, crea una instancia de LazyLayoutCacheWindow y pásala a tu lista o cuadrícula diferida. Mide el rendimiento de tu app con diferentes tamaños de ventana de caché, por ejemplo, el 50% de la ventana gráfica. 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 pausable

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 recuperación previa del diseño diferido. Deberías ver el mayor beneficio con elementos complejos con tiempos de composición más largos. 

image.png

Más optimizaciones del rendimiento de Compose

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

Se mejoraron varias APIs que usan corrutinas de forma interna. Por ejemplo, cuando se usan DraggableClickable, los desarrolladores deberían ver tiempos de reacción más rápidos y recuentos de asignación mejorados.

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

Otra mejora en el rendimiento es el uso de valores almacenados en caché cuando se observan posiciones a través de onPlaced().

Prefetch text in the background

A partir de la versión 1.9, Compose agrega la capacidad de realizar una recuperación previa del texto en un subproceso en segundo plano. Esto te permite precalentar las memorias caché para habilitar un diseño de texto más rápido y es relevante para el rendimiento de la renderización de la app. Durante el diseño, el texto debe pasar al framework de Android, donde se completa una caché de palabras. De forma predeterminada, se ejecuta en el subproceso de IU. Descargar la recuperación previa y propagar la caché de palabras en un subproceso en segundo plano puede acelerar el diseño, especialmente para textos más largos. Para realizar la recuperación previa en un subproceso en segundo plano, puedes pasar un ejecutor personalizado a cualquier elemento componible que use BasicText de forma interna pasando un LocalBackgroundTextMeasurementExecutor a un CompositionLocalProvider de la siguiente manera.

  val defaultTextMeasurementExecutor = Executors.newSingleThreadExecutor()

CompositionLocalProvider(

    LocalBackgroundTextMeasurementExecutor provides DefaultTextMeasurementExecutor

) {

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


}

Según el texto, esto puede mejorar el rendimiento de la renderización del texto. Para asegurarte de que mejore el rendimiento de renderización de tu app, realiza comparativas y compara los resultados.

Consideraciones sobre el rendimiento del trabajo en segundo plano

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

  • Subir eventos analíticos periódicamente
  • Sincronización de datos entre un servicio de backend y una base de datos
  • Procesamiento de contenido multimedia (es decir, cambiar el tamaño o comprimir imágenes)

Un desafío clave al ejecutar estas tareas es equilibrar el rendimiento y la eficiencia energética. WorkManager te permite lograr este equilibrio. Está diseñado para ser eficiente en el consumo de energía y permitir que el trabajo se posponga hasta una ventana de ejecución óptima influenciada 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 diseñaron específicamente teniendo en cuenta ciertos recorridos del usuario principales (CUJ) comunes.  

Consulta la página de destino de Trabajo en segundo plano para ver una lista de algunos de estos,  incluida la actualización de un widget y la obtención de la ubicación en segundo plano.

Herramientas de depuración locales para el trabajo en segundo plano: situaciones comunes

Para depurar el trabajo en segundo plano y comprender por qué una tarea puede haberse retrasado o fallado, necesitas visibilidad sobre cómo el sistema programó tus tareas. 

Para ayudarte con esto, WorkManager tiene varias herramientas relacionadas para ayudarte a depurar de forma local y optimizar el rendimiento (algunas de estas también funcionan para JobScheduler). A continuación, se incluyen algunas situaciones comunes que puedes encontrar cuando usas WorkManager y una explicación de las herramientas que puedes usar para depurarlas.

Cómo depurar por qué no se ejecuta el trabajo programado

El retraso o la falta de ejecución del trabajo programado pueden deberse a varios factores, como el incumplimiento de las restricciones especificadas o la imposición de restricciones por parte del sistema

El primer paso para investigar por qué no se ejecuta el trabajo programado es confirmar que el trabajo se programó correctamente. Después de confirmar el estado de la programación, determina si hay restricciones o condiciones previas no satisfechas que impidan la ejecución del trabajo.

Existen varias herramientas para depurar esta situación.

Inspector de tareas en segundo plano

El Inspector de tareas en segundo plano es una herramienta potente integrada directamente en Android Studio. Proporciona una representación visual de todas las tareas de WorkManager y sus estados asociados (En ejecución, En cola, Con errores, Completada). 

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 programó, pero aún está en espera de ejecutarse.

Beneficios: Además de proporcionar 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 una vista de gráfico que puede visualizar si una tarea anterior que falló pudo haber afectado la ejecución de la tarea siguiente.

image.png

Vista de lista del Inspector de tareas en segundo plano

image.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 activos de JobScheduler (que incluye los Workers de WorkManager) junto con las restricciones especificadas y las impuestas por el sistema. También devuelve el historial de trabajos. 

Usa esta opción si deseas ver tu trabajo programado y las restricciones asociadas de una manera diferente. 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 app tiene varios trabajadores, la actualización a WorkManager 2.10.0 te permitirá ver los nombres de los trabajadores y distinguirlos fácilmente:

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

Beneficios: Este comando es útil para comprender si hubo alguna restricción impuesta por el sistema, que no puedes determinar con el Inspector de tareas en segundo plano. Por ejemplo, esto devolverá el segmento en espera de la app, lo que puede afectar la ventana en la que se completa el trabajo programado.

Habilita el registro de depuración

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

Beneficios: Esto te permite obtener visibilidad sobre cuándo se programa el trabajo, cuándo se cumplen las restricciones y cuándo ocurren los eventos del ciclo de vida. Puedes consultar estos registros mientras desarrollas tu app.

WorkInfo.StopReason

Si observas un rendimiento impredecible con un trabajador específico, puedes observar de forma programática el motivo por el que se detuvo el trabajador en el intento de ejecución anterior con WorkInfo.getStopReason

Es una buena práctica configurar tu app para que observe WorkInfo con getWorkInfoByIdFlow y, así, identificar si tu trabajo se ve afectado por restricciones en segundo plano, restricciones, tiempos de espera frecuentes o incluso si el usuario lo detuvo.

Beneficios: Puedes usar WorkInfo.StopReason para recopilar datos de campo sobre el rendimiento de tus trabajadores.

Cómo depurar la duración alta del bloqueo de activación atribuida a WorkManager que Android vitals marcó

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

Panel de Android vitals

Primero, confirma en el panel de Android vitals sobre bloqueos de activación excesivos que la duración alta del bloqueo de activación provenga de WorkManager y no de una alarma o de otro bloqueo de activación. Puedes usar la documentación de Cómo identificar los bloqueos de activación creados por otras APIs para comprender qué bloqueos de activación se mantienen debido a WorkManager. 

Perfetto

Perfetto es una herramienta para analizar registros del sistema. Cuando lo usas para depurar WorkManager específicamente, puedes ver la sección "Estado del dispositivo" para saber cuándo comenzó tu trabajo, cuánto tiempo se ejecutó y cómo contribuye al consumo de energía. 

En el registro “Estado del dispositivo: Trabajos”, puedes ver los trabajadores que se ejecutaron y sus bloqueos de activación asociados.

deviceState.png

Sección Device State en Perfetto, que muestra la ejecución de CleanupWorker y BlurWorker.

Recursos

Consulta la página Debug WorkManager para obtener 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 de forma práctica y obtener más información sobre la depuración de WorkManager, consulta el codelab Advanced WorkManager and Testing.

Próximos pasos

Hoy fuimos más allá de la reducción de código y exploramos cómo Android Runtime y Jetpack Compose renderizan tu app. Ya sea que se trate de compilar previamente rutas críticas con Perfiles de Baseline o de suavizar los estados de desplazamiento con las nuevas funciones de Compose 1.9 y 1.10, estas herramientas se enfocan en la sensación de tu app. Además, profundizamos en las prácticas recomendadas para depurar el trabajo en segundo plano.

Pregúntale a Android

El viernes, organizaremos una sesión de preguntas y respuestas en vivo sobre el rendimiento. Haz tus preguntas ahora con #AskAndroid y obtén respuestas de los expertos.

El desafío

El lunes te desafiamos a habilitar R8. Hoy te pedimos que generes un perfil de Baseline para tu app.

Con Android Studio Otter, el asistente del módulo Baseline Profile Generator hace que esto sea más fácil que nunca. Elige el recorrido del usuario más importante, incluso si solo se trata del inicio de la app y el acceso, y genera un perfil.

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

Comparte las mejoras en el tiempo de inicio en las redes sociales con #optimizationEnabled.

Sintoniza mañana

Redujiste tu app con R8 y optimizaste tu tiempo de ejecución con la optimización guiada por perfil. Pero, ¿cómo demuestras estos logros a tus interesados? ¿Y cómo detectas las regresiones antes de que lleguen a producción?

Acompáñanos mañana en el Día 4: La guía de nivelación del rendimiento, en la que explicaremos exactamente cómo medir tu éxito, desde los datos de campo en Play Vitals hasta el registro local detallado con Perfetto.

Escrito por:

Seguir leyendo