En este documento, se muestran las prácticas recomendadas para ayudar a diagnosticar problemas y garantizar que tus perfiles de Baseline funcionen correctamente para proporcionar el mayor beneficio.
Problemas de compilación
Si copiaste el ejemplo de perfiles de Baseline en la app de ejemplo de Now in Android, es posible que experimentes pruebas fallidas durante la tarea de perfiles de Baseline en las que se indique que las pruebas no se pueden ejecutar en un emulador:
./gradlew assembleDemoRelease
Starting a Gradle Daemon (subsequent builds will be faster)
Calculating task graph as no configuration cache is available for tasks: assembleDemoRelease
Type-safe project accessors is an incubating feature.
> Task :benchmarks:pixel6Api33DemoNonMinifiedReleaseAndroidTest
Starting 14 tests on pixel6Api33
com.google.samples.apps.nowinandroid.foryou.ScrollForYouFeedBenchmark > scrollFeedCompilationNone[pixel6Api33] FAILED
java.lang.AssertionError: ERRORS (not suppressed): EMULATOR
WARNINGS (suppressed):
...
Las fallas se producen porque Now in Android usa un dispositivo administrado por Gradle para la generación de perfiles de Baseline. Se esperan fallas, porque, por lo general, no debes ejecutar comparativas de rendimiento en un emulador. Sin embargo, como no recopilas métricas de rendimiento cuando generas perfiles de Baseline, puedes ejecutar la recopilación de perfiles de Baseline en emuladores para tu comodidad. Para usar perfiles de Baseline con un emulador, realiza la compilación y la instalación desde la línea de comandos, y establece un argumento para habilitar las reglas de los perfiles de Baseline:
installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
Como alternativa, puedes crear una configuración de ejecución personalizada en Android Studio para habilitar los perfiles de Baseline en emuladores seleccionando Run > Edit Configurations:
Problemas de instalación
Verifica que el APK o el AAB que compilas sea de una variante de compilación que incluya perfiles de Baseline. La manera más fácil de verificar esto es abrir el APK en Android Studio seleccionando Build > Analyze APK abriendo el APK y buscando el perfil en el archivo /assets/dexopt/baseline.prof
:
Los perfiles de Baseline deben compilarse en el dispositivo que ejecuta la app. En el caso de las instalaciones en la tienda de aplicaciones y las apps instaladas con PackageInstaller
, la compilación en el dispositivo se realiza como parte del proceso de instalación de la app. Sin embargo, cuando la app se transfiere desde Android Studio o con herramientas de línea de comandos, la biblioteca de Jetpack ProfileInstaller
es responsable de poner en cola los perfiles para su compilación durante el siguiente proceso de optimización de DEX en segundo plano. En esos casos, si deseas asegurarte de que se usen tus perfiles de Baseline, es posible que debas forzar la compilación de perfiles de Baseline. ProfileVerifier
te permite consultar el estado de instalación y compilación del perfil, como se muestra en el siguiente ejemplo:
Kotlin
private const val TAG = "MainActivity" class MainActivity : ComponentActivity() { ... override fun onResume() { super.onResume() lifecycleScope.launch { logCompilationStatus() } } private suspend fun logCompilationStatus() { withContext(Dispatchers.IO) { val status = ProfileVerifier.getCompilationStatusAsync().await() when (status.profileInstallResultCode) { RESULT_CODE_NO_PROFILE -> Log.d(TAG, "ProfileInstaller: Baseline Profile not found") RESULT_CODE_COMPILED_WITH_PROFILE -> Log.d(TAG, "ProfileInstaller: Compiled with profile") RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING -> Log.d(TAG, "ProfileInstaller: App was installed through Play store") RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST -> Log.d(TAG, "ProfileInstaller: PackageName not found") RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ -> Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read") RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE -> Log.d(TAG, "ProfileInstaller: Can't write cache file") RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") else -> Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued") } } }
Java
public class MainActivity extends ComponentActivity { private static final String TAG = "MainActivity"; @Override protected void onResume() { super.onResume(); logCompilationStatus(); } private void logCompilationStatus() { ListeningExecutorService service = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor()); ListenableFuture<ProfileVerifier.CompilationStatus> future = ProfileVerifier.getCompilationStatusAsync(); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(CompilationStatus result) { int resultCode = result.getProfileInstallResultCode(); if (resultCode == RESULT_CODE_NO_PROFILE) { Log.d(TAG, "ProfileInstaller: Baseline Profile not found"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE) { Log.d(TAG, "ProfileInstaller: Compiled with profile"); } else if (resultCode == RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING) { Log.d(TAG, "ProfileInstaller: App was installed through Play store"); } else if (resultCode == RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST) { Log.d(TAG, "ProfileInstaller: PackageName not found"); } else if (resultCode == RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ) { Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read"); } else if (resultCode == RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE) { Log.d(TAG, "ProfileInstaller: Can't write cache file"); } else if (resultCode == RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else { Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued"); } } @Override public void onFailure(Throwable t) { Log.d(TAG, "ProfileInstaller: Error getting installation status: " + t.getMessage()); } }, service); } }
Los siguientes códigos de resultado proporcionan sugerencias sobre la causa de algunos problemas:
RESULT_CODE_COMPILED_WITH_PROFILE
- Se instala, compila y usa el perfil cada vez que se ejecuta la app. Este es el resultado que deseas ver.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
- No se encontró ningún perfil en el APK o el AAB que se está ejecutando. Asegúrate de usar una variante de compilación que incluya perfiles de Baseline si ves este error y de que el APK contenga un perfil.
RESULT_CODE_NO_PROFILE
- No se instaló ningún perfil para esta app cuando se instaló a través de la tienda de aplicaciones o el administrador de paquetes. El motivo principal de esto como código de error es que el instalador de perfiles no se ejecutó debido a que
ProfileInstallerInitializer
estaba inhabilitado. Ten en cuenta que, cuando se informa este error, aún se encuentra un perfil incorporado en el APK de la aplicación. Cuando no se encuentra un perfil incorporado, el código de error que se muestra esRESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
. RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
- Un perfil se encuentra en el APK o AAB, y se pone en cola para la compilación. Cuando
ProfileInstaller
instala un perfil, este se pone en cola para la compilación la próxima vez que el sistema ejecute la optimización DEX en segundo plano. El perfil no estará activo hasta que se complete la compilación. No intentes comparar tus perfiles de Baseline hasta que se complete la compilación. Es posible que debas forzar la compilación de los perfiles de Baseline. Este error no se producirá cuando se instale la app desde la tienda de aplicaciones o el administrador de paquetes en dispositivos que ejecuten Android 9 (nivel de API 28) y versiones posteriores, ya que la compilación se realiza durante la instalación. RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
- Se instaló un perfil que no coincide y se compiló la app con él.
Este es el resultado de la instalación a través de Google Play Store o el administrador de paquetes.
Ten en cuenta que este resultado difiere de
RESULT_CODE_COMPILED_WITH_PROFILE
porque el perfil no coincidente solo compilará los métodos que todavía se compartan entre el perfil y la app. El perfil es efectivamente más pequeño de lo esperado y se compilarán menos métodos de los que se incluyeron en el perfil de Baseline. RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier
no puede escribir el archivo de caché de los resultados de la verificación. Esto puede suceder porque hay algún problema con los permisos de la carpeta de la app o si no hay suficiente espacio libre en el disco en el dispositivo.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION
- ProfileVerifier
is running on an unsupported API version of Android. ProfileVerifier
solo admite Android 9 (nivel de API 28) y versiones posteriores. RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST
- Se arroja una
PackageManager.NameNotFoundException
cuando se consulta elPackageManager
del paquete de la app. Esto debería ocurrir rara vez. Intenta desinstalar la app y volver a instalar todo. RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
- Existe un archivo de caché de resultados de verificación anterior, pero no se puede leer. Esto rara vez debería suceder. Intenta desinstalar la app y volver a instalar todo.
Usa ProfileVerifier en producción
En producción, puedes usar ProfileVerifier
junto con bibliotecas de informes de estadísticas, como Google Analytics para Firebase, para generar eventos de estadísticas que indiquen el estado del perfil. Por ejemplo, esto te alerta rápidamente si se lanza una nueva versión de la app que no contiene perfiles de Baseline.
Fuerza la compilación de perfiles de Baseline
Si el estado de compilación de tus perfiles de Baseline es RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
, puedes forzar la compilación inmediata a través de adb
:
adb shell cmd package compile -r bg-dexopt PACKAGE_NAME
Verifica el estado de compilación sin ProfileVerifier
Si no usas ProfileVerifier
, puedes verificar el estado de compilación con adb
, aunque no proporciona estadísticas tan detalladas como ProfileVerifier
:
adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME
El uso de adb
produce algo similar a lo siguiente:
[com.google.samples.apps.nowinandroid.demo]
path: /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/base.apk
arm64: [status=speed-profile] [reason=bg-dexopt] [primary-abi]
[location is /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/oat/arm64/base.odex]
El valor de estado indica el estado de compilación del perfil y es uno de los siguientes valores:
Estado de compilación | Significado |
---|---|
speed‑profile |
Existe un perfil compilado y está en uso. |
verify |
No existe ningún perfil compilado. |
Un estado verify
no significa que el APK o el AAB no contengan un perfil, ya que se puede poner en cola para la compilación de la siguiente tarea de optimización de DEX en segundo plano.
El valor del motivo indica qué activa la compilación del perfil y es uno de los siguientes valores:
Motivo | Significado |
---|---|
install‑dm
|
Cuando se instala la app, se compila un perfil de Baseline manualmente o a través de Google Play. |
bg‑dexopt
|
Se compiló un perfil mientras tu dispositivo estaba inactivo. Puede ser un perfil de Baseline o un perfil recopilado durante el uso de la app. |
cmdline
|
La compilación se activó con adb. Puede ser un perfil de Baseline o un perfil recopilado durante el uso de la app. |
Problemas de rendimiento
En esta sección, se muestran algunas prácticas recomendadas para definir y comparar correctamente tus perfiles de Baseline para aprovecharlos al máximo.
Compara correctamente las métricas de inicio
Tus perfiles de Baseline serán más eficaces si tus métricas de inicio están bien definidas. Las dos métricas clave son el tiempo para la visualización inicial (TTID) y el tiempo para la visualización completa (TTFD).
El TTID ocurre cuando la app dibuja su primer fotograma. Es importante que sea lo más breve posible, ya que mostrar algo le indica al usuario que se está ejecutando la app. Incluso puedes mostrar un indicador de progreso indeterminado para indicar que la app es responsiva.
TTFD es el momento en que se puede interactuar con la app. Es importante que sea lo más breve posible para evitar que el usuario se frustre. Si indicas el TTFD correctamente, le haces saber al sistema que el código que se ejecuta en el camino al TTFD forma parte del inicio de la app. Como resultado, es más probable que el sistema coloque este código en el perfil.
Mantén los valores de TTID y TTFD tan bajos como sea posible para que tu app tenga una mayor capacidad de respuesta.
El sistema puede detectar el TTID, mostrarlo en Logcat y notificarlo como parte de las comparativas de inicio. Sin embargo, el sistema no puede determinar el TTFD y es responsabilidad de la app informar cuando alcanza un estado interactivo de obtenida por completo. Para ello, llama a reportFullyDrawn()
o a ReportDrawn
si usas Jetpack Compose. Si tienes varias tareas en segundo plano que debes completar antes de que la app se considere completamente dibujada, puedes usar FullyDrawnReporter
, como se describe enMejora la precisión de los tiempos de inicio.
Perfiles de biblioteca y perfiles personalizados
Cuando se compara el impacto de los perfiles, puede ser difícil separar beneficios de los perfiles de tu app provenientes de perfiles aportados por las bibliotecas, como Bibliotecas de Jetpack Cuando compilas tu APK, el complemento de Android para Gradle agrega en las dependencias de bibliotecas, así como en tu perfil personalizado. Está bien para optimizar el rendimiento general y se recomienda para tus compilaciones de lanzamiento. Sin embargo, hace que sea difícil medir cuánta mejora adicional de rendimiento se obtiene desde tu perfil personalizado.
Una forma rápida de ver manualmente la optimización adicional que proporciona tu es quitarlo y ejecutar tus comparativas. Luego, reemplázala y ejecuta tu comparativas nuevamente. La comparación de ambas te mostrará las optimizaciones que proporciona los perfiles de biblioteca solamente y los perfiles de biblioteca más el perfil personalizado.
Una forma automatizable de comparar perfiles es creando una nueva variante de compilación que
contiene solo los perfiles de la biblioteca y no el perfil personalizado. Comparar
comparativas de esta variante a la variante de lanzamiento que contiene tanto
perfiles de biblioteca y tus perfiles personalizados. En el siguiente ejemplo, se muestra cómo
para configurar la variante que solo incluya perfiles de biblioteca. Agregar una variante nueva
llamado releaseWithoutCustomProfile
al módulo de consumidor del perfil, que es
por lo general, el módulo de tu app:
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile(project(":baselineprofile")) } baselineProfile { variants { create("release") { from(project(":baselineprofile")) } } }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile ':baselineprofile"' } baselineProfile { variants { release { from(project(":baselineprofile")) } } }
En el ejemplo de código anterior, se quita la dependencia baselineProfile
de todos los
variantes y la aplica de forma selectiva solo a la variante release
. Puede parecer
parece contradictorio de que aún se siguen agregando los perfiles de biblioteca cuando
se quita la dependencia en el módulo del generador de perfiles. Sin embargo, en este módulo
solo es responsable de generar tu perfil personalizado. El complemento de Android para Gradle
sigue ejecutándose para todas las variantes y es responsable de incluir
perfiles de biblioteca.
También debes agregar la nueva variante al módulo de generador de perfiles. En este
Por ejemplo, el módulo del productor se llama :baselineprofile
.
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") {} ... } ... }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile {} ... } ... }
Cuando ejecutes las comparativas desde Android Studio, selecciona una
Variante releaseWithoutCustomProfile
para medir el rendimiento solo con la biblioteca
o selecciona una variante de release
para medir el rendimiento con la biblioteca
y perfiles personalizados.
Evita el inicio de apps vinculadas a E/S
Si tu app realiza muchas llamadas de E/S o de red durante el inicio, esto puede afectar negativamente tanto el tiempo de inicio de la app como la precisión de las comparativas de inicio. Estas llamadas pesadas pueden tardar una cantidad de tiempo indeterminada que puede variar con el paso del tiempo y hasta entre iteraciones de la misma comparativa. Las llamadas de E/S suelen ser mejores que las de red, ya que estas últimas pueden verse afectadas por factores externos al dispositivo y del mismo dispositivo. Evita las llamadas de red durante el inicio. Cuando no se pueda evitar usar uno o el otro, utiliza E/S.
Te recomendamos que la arquitectura de tu app admita el inicio de la app sin llamadas de red o E/S, incluso si solo se usa cuando se realiza la comparativa de inicio. Esto ayuda a garantizar la menor variabilidad posible entre las diferentes iteraciones de tus comparativas.
Si tu app usa Hilt, puedes proporcionar conexiones de E/S falsas cuando se generan comparativas en microcomparativas y Hilt.
Abarca todos los recorridos importantes del usuario
Es importante abarcar con precisión todos los recorridos importantes del usuario en la generación de perfiles de Baseline. Los perfiles de Baseline no mejorarán los recorridos del usuario que no estén cubiertos. Los perfiles de referencia más efectivos incluyen todos los recorridos del usuario de inicio comunes, así como los recorridos del usuario en la app sensibles al rendimiento, como el desplazamiento por listas.