En el desarrollo moderno de Android, los usuarios esperan que las aplicaciones sean pequeñas, rápidas y seguras. La herramienta principal del sistema de compilación de Android para conseguirlo es el optimizador R8 , el compilador que gestiona la eliminación de código y recursos inactivos para la reducción, el cambio de nombre o la minificación del código, y la optimización de la aplicación.
Habilitar R8 es un paso fundamental para preparar una aplicación para su lanzamiento, pero requiere que los desarrolladores proporcionen instrucciones en forma de reglas de conservación.
Después de leer este artículo, consulta el vídeo de la semana de Performance Spotlight sobre cómo habilitar, depurar y solucionar problemas del optimizador R8 en YouTube.
Por qué son necesarias las reglas de conservación
La necesidad de escribir reglas de conservación se debe a un conflicto fundamental: R8 es una herramienta de análisis estático, pero las aplicaciones Android suelen depender de patrones de ejecución dinámicos, como la reflexión o las llamadas dentro y fuera del código nativo mediante JNI (Java Native Interface).
R8 crea un gráfico del código usado analizando las llamadas directas. Cuando se accede al código de forma dinámica, el análisis estático de R8 no puede predecirlo, por lo que identificará ese código como sin usar y lo eliminará, lo que provocará fallos en el tiempo de ejecución.
Una regla de conservación es una instrucción explícita para el compilador R8 que indica lo siguiente: "Esta clase, método o campo específico es un punto de entrada al que se accederá de forma dinámica en el tiempo de ejecución. Debes conservarlo, aunque no encuentres una referencia directa a él".
Consulta la guía oficial para obtener más información sobre las reglas de conservación.
Dónde escribir reglas de Keep
Las reglas de conservación personalizadas de una aplicación se escriben en un archivo de texto. Por convención, este archivo se llama proguard-rules.pro y se encuentra en la raíz del módulo de la aplicación o de la biblioteca. Este archivo se especifica en el tipo de compilación release del archivo build.gradle.kts de tu módulo.
release {
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}Usar el archivo predeterminado correcto
El método getDefaultProguardFile importa un conjunto de reglas predeterminado proporcionado por el SDK para Android. Si usas el archivo incorrecto, es posible que tu aplicación no esté optimizada. Asegúrate de usar proguard-android-optimize.txt. Este archivo proporciona las reglas Keep predeterminadas para los componentes estándar de Android y habilita las optimizaciones de código de R8. La versión antigua de proguard-android.txt solo proporciona las reglas Keep, pero no habilita las optimizaciones de R8.
Como se trata de un problema de rendimiento grave, vamos a empezar a advertir a los desarrolladores sobre el uso del archivo incorrecto a partir de la actualización con nuevas funciones 3 de Android Studio Narwhal. A partir de la versión 9.0 del complemento de Android para Gradle, ya no admitimos el archivo proguard-android.txtobsoleto. Por lo tanto, asegúrate de actualizar a la versión optimizada.
Cómo escribir reglas de Keep
Una regla de conservación consta de tres partes principales:
- Una opción como
-keepo-keepclassmembers - Modificadores opcionales, como
allowshrinking - Una especificación de clase que define el código que se debe buscar
Para ver la sintaxis completa y ejemplos, consulta las directrices para añadir reglas de conservación.
Anti-patrones de Keep Rule
Es importante conocer las prácticas recomendadas, pero también los antipatrones. Estos antipatrones suelen surgir de malentendidos o de atajos para solucionar problemas, y pueden ser catastróficos para el rendimiento de una compilación de producción.
Opciones globales
Estas marcas son interruptores globales que nunca se deben usar en una compilación de lanzamiento. Solo se usan para depuraciones temporales con el fin de aislar un problema.
Si usas -dontotptimize, se inhabilitan de forma eficaz las optimizaciones de rendimiento de R8, lo que provoca que la aplicación sea más lenta.
Si usas -dontobfuscate, se inhabilitará la opción de cambiar el nombre de todos los elementos, mientras que si usas -dontshrink, se desactivará la eliminación del código obsoleto. Ambas reglas globales aumentan el tamaño de la aplicación.
Evite usar estas marcas globales en un entorno de producción siempre que sea posible para ofrecer una experiencia de usuario de la aplicación más eficiente.
Reglas de conservación demasiado generales
La forma más sencilla de anular las ventajas de R8 es escribir reglas de conservación demasiado amplias. Las reglas como la que se muestra a continuación indican al optimizador R8 que no reduzca, no ofusque ni optimice ninguna clase de este paquete ni ninguno de sus subpaquetes. De esta forma, se eliminan por completo las ventajas de R8 para todo el paquete. Prueba a escribir reglas de Keep más específicas.
-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMS
El operador de inversión (!)
El operador de inversión (!) parece una forma eficaz de excluir un paquete de una regla. Pero no es tan sencillo. Veamos este ejemplo:
-keep class !com.example.my_package.** { *; } // USE WITH CAUTION
Puede que pienses que esta regla significa "no conserves las clases encom.example.package", pero en realidad significa "conserva todas las clases, métodos y propiedades de toda la aplicación que no estén en com.example.package". Si te ha sorprendido, te recomendamos que busques negaciones en tu configuración de R8.
Reglas redundantes para componentes Android
Otro error habitual es añadir manualmente reglas Keep para Activities, Services o BroadcastReceivers de tu aplicación. Esto es innecesario. El archivo proguard-android-optimize.txt predeterminado ya incluye las reglas pertinentes para que estos componentes estándar de Android funcionen sin necesidad de configuración.
Además, muchas bibliotecas tienen sus propias reglas de conservación. Por lo tanto, no debería tener que escribir sus propias reglas para estos casos. Si hay algún problema con Keep Rules de una biblioteca que estés usando, lo mejor es que te pongas en contacto con el autor de la biblioteca para saber cuál es el problema.
Prácticas recomendadas de Keep Rule
Ahora que ya sabes lo que no debes hacer, vamos a hablar de las prácticas recomendadas.
Escribir reglas de Keep específicas
Las buenas reglas de conservación deben ser lo más específicas y concretas posible. Solo deben conservar lo necesario para que R8 pueda optimizar el resto.
| Regla | Calidad |
|---|---|
| Bajo: mantiene todo el paquete y sus subpaquetes. |
| Bajo: mantiene toda una clase, que probablemente siga siendo demasiado ancha. |
| Alto: solo se conservan los métodos y las propiedades relevantes de una clase específica. |
Usar ancestros comunes
En lugar de escribir reglas de conservación independientes para varios modelos de datos diferentes, escribe una regla que se dirija a una clase o interfaz base común. La regla que se muestra a continuación indica a R8 que conserve los miembros de las clases que implementen esta interfaz y es muy escalable.
# Keep all fields of any class that implements SerializableModel -keepclassmembers class * implements com.example.models.SerializableModel { <fields>; }
Usar anotaciones para segmentar varias clases
Crea una anotación personalizada (por ejemplo, @Serialize) y úsala para "etiquetar" las clases cuyos campos deban conservarse. Se trata de otro patrón limpio, declarativo y altamente escalable. También puedes crear reglas de conservación para las anotaciones que ya tengas de los frameworks que estés usando.
# Keep all fields of any class annotated with @Serialize -keepclassmembers class * { @com.example.annotations.Serialize <fields>; }
Elegir la opción de Keep adecuada
La opción Conservar es la parte más importante de la regla. Si eliges la opción incorrecta, se inhabilitará la optimización innecesariamente.
| Mantener opción | Qué hace |
-keep | Impide que se eliminen o cambien de nombre la clase y los miembros mencionados en la declaración . |
-keepclassmembers | Impide que se eliminen o cambien el nombre de los miembros especificados, pero permite que se elimine la clase en sí, aunque solo en las clases que no se hayan eliminado de otro modo. |
-keepclasseswithmembers | Una combinación: mantiene la clase y sus miembros solo si están presentes todos los miembros especificados. |
Puedes consultar más información sobre la opción de conservar en nuestra documentación sobre las opciones de conservación.
Permitir la optimización con modificadores
Los modificadores como allowshrinking y allowobfuscation relajan una regla -keep amplia, lo que devuelve la capacidad de optimización a R8. Por ejemplo, si una biblioteca antigua te obliga a usar -keep en toda una clase, puedes recuperar parte de la optimización permitiendo la reducción y la ofuscación:
# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it. -keep,allowshrinking,allowobfuscation class com.example.LegacyClass
Añadir opciones globales para optimizar aún más
Además de las reglas de conservación, puedes añadir marcas globales al archivo de configuración de R8 para fomentar aún más la optimización.
-repackageclasses es una opción potente que indica a R8 que mueva todas las clases ofuscadas a un solo paquete. De esta forma, se ahorra espacio significativo en el archivo DEX, ya que se eliminan las cadenas de nombres de paquetes redundantes.
-allowaccessmodification permite que R8 amplíe el acceso (por ejemplo, de private a public) para habilitar una inserción más agresiva. Ahora, esta opción está habilitada de forma predeterminada al usar proguard-android-optimize.txt.
Advertencia: Los autores de bibliotecas nunca deben añadir estas marcas de optimización globales a sus reglas de consumidor, ya que se aplicarían de forma obligatoria a toda la aplicación.
Para que quede aún más claro, en la versión 9.0 del complemento de Android para Gradle vamos a empezar a ignorar por completo las marcas de optimización globales de las bibliotecas.
Prácticas recomendadas para bibliotecas
Todas las aplicaciones Android dependen de las bibliotecas de una forma u otra. Por eso, vamos a hablar de las prácticas recomendadas para las bibliotecas.
Para desarrolladores de bibliotecas
Si tu biblioteca usa reflexión o JNI, tienes la responsabilidad de proporcionar las reglas Keep necesarias a sus consumidores. Estas reglas se colocan en un archivo consumer-rules.pro, que se incluye automáticamente en el archivo AAR de la biblioteca.
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
...
}Para los consumidores de la biblioteca
Filtrar reglas de Keep problemáticas
Si tienes que usar una biblioteca que incluya reglas de conservación problemáticas, puedes filtrarlas en tu archivo build.gradle.kts a partir de la versión 9.0 del complemento de Android para Gradle. De esta forma, se indica a R8 que ignore las reglas procedentes de una dependencia específica.
release {
optimization.keepRules {
// Ignore all consumer rules from this specific library
it.ignoreFrom("com.somelibrary:somelibrary")
}
}La mejor regla de conservación es no tener ninguna
La mejor estrategia de configuración de R8 es eliminar por completo la necesidad de escribir reglas Keep. En muchas aplicaciones, esto se puede conseguir eligiendo bibliotecas modernas que favorezcan la generación de código en lugar de la reflexión. Con la generación de código, el optimizador puede determinar más fácilmente qué código se usa realmente en el tiempo de ejecución y qué código se puede eliminar. Además, al no usar ninguna reflexión dinámica, no hay puntos de entrada "ocultos" y, por lo tanto, no se necesitan reglas Keep. Cuando elijas una biblioteca nueva, siempre es preferible una solución que utilice la generación de código en lugar de la reflexión.
Para obtener más información sobre cómo elegir bibliotecas, consulta Elegir bibliotecas de forma adecuada.
Depurar y solucionar problemas de la configuración de R8
Cuando R8 elimina código que debería haber conservado o tu APK es más grande de lo esperado, usa estas herramientas para diagnosticar el problema.
Buscar reglas de conservación duplicadas y globales
Como R8 combina reglas de docenas de fuentes, puede ser difícil saber cuál es el conjunto de reglas "final". Si añades esta marca a tu archivo proguard-rules.pro, se generará un informe completo:
# Outputs the final, merged set of rules to the specified file -printconfiguration build/outputs/logs/configuration.txt
Puedes buscar en este archivo reglas redundantes o rastrear una regla problemática (como -dontoptimize) hasta la biblioteca específica que la incluía.
Pregunta a R8: ¿Por qué conserváis esta información?
Si una clase que esperabas que se eliminara sigue en tu aplicación, R8 puede decirte por qué. Solo tienes que añadir esta regla:
# Asks R8 to explain why it's keeping a specific class class com.example.MyUnusedClass -whyareyoukeeping
Durante la compilación, R8 imprimirá la cadena exacta de referencias que ha provocado que se conserve esa clase, lo que te permitirá rastrear la referencia y ajustar tus reglas.
Para ver una guía completa, consulta la sección Solucionar problemas de R8.
Pasos siguientes
R8 es una herramienta muy útil para mejorar el rendimiento de las aplicaciones Android. Su eficacia depende de que se entienda correctamente su funcionamiento como motor de análisis estático.
Si escribes reglas específicas a nivel de miembro, aprovechas los ancestros y las anotaciones, y eliges cuidadosamente las opciones de conservación adecuadas, puedes conservar exactamente lo que necesitas. La práctica más avanzada consiste en eliminar por completo la necesidad de reglas eligiendo bibliotecas modernas basadas en la generación de código en lugar de sus predecesoras basadas en la reflexión.
Mientras sigues la Semana de la Puesta a Punto del Rendimiento, no te pierdas el vídeo de hoy en YouTube y continúa con nuestro reto de la versión 8. Usa #optimizationEnabled si tienes alguna duda sobre cómo habilitar o solucionar problemas de R8. Estamos aquí para ayudarte.
Es el momento de comprobar las ventajas por ti mismo.
Te proponemos que habilites el modo completo de R8 en tu aplicación hoy mismo.
- Para empezar, consulta nuestras guías para desarrolladores: Habilitar la optimización de aplicaciones.
- Comprueba si sigues usando
proguard-android.txty sustitúyelo porproguard-android-optimize.txt. - Después, mide el impacto. No te limites a notar la diferencia, verifícala. Mide las mejoras de rendimiento adaptando el código de nuestra aplicación de ejemplo Macrobenchmark en GitHub para medir los tiempos de inicio antes y después.
Estamos seguros de que notarás una mejora significativa en el rendimiento de tu aplicación.
Aprovecha para usar la etiqueta #AskAndroid y enviar tus preguntas. Nuestros expertos monitorizan y responden a tus preguntas durante toda la semana.
Mañana hablaremos sobre la optimización guiada por perfil con perfiles de línea de base y de inicio, explicaremos cómo ha mejorado el rendimiento de renderización de Compose en las versiones anteriores y compartiremos consideraciones sobre el rendimiento del trabajo en segundo plano.
Seguir leyendo
-
Noticias sobre productos
Nos complace anunciar importantes actualizaciones de nuestros recursos de diseño, que te ofrecen la guía completa que necesitas para crear aplicaciones Android adaptables y de alta calidad en todos los factores de forma. Ahora tenemos una guía sobre la experiencia de escritorio y una galería de diseño de Android renovada.
Ivy Knight • Tiempo de lectura: 2 min
-
Noticias sobre productos
Se ha lanzado la primera versión alfa de Room 3.0. Room 3.0 es una versión principal de la biblioteca que introduce cambios importantes y se centra en Kotlin Multiplatform (KMP). Además, añade compatibilidad con JavaScript y WebAssembly (WASM) a la compatibilidad con Android, iOS y JVM para ordenadores.
Daniel Santiago Rivera • Tiempo de lectura: 4 min
-
Noticias sobre productos
Hoy nos complace compartir cómo estamos incorporando la optimización automática dirigida por comentarios (AutoFDO) al kernel de Android para ofrecer a los usuarios mejoras significativas en el rendimiento.
Yabin Cui • Tiempo de lectura: 4 min
Mantente al día
Recibe cada semana en tu bandeja de entrada las últimas novedades sobre el desarrollo para Android.