Novedades sobre productos
Configura y soluciona problemas de las reglas de conservación de R8
Lectura de 7 min
En el desarrollo moderno de Android, entregar una aplicación pequeña, rápida y segura es una expectativa fundamental de los usuarios. La herramienta principal del sistema de compilación de Android para lograr esto es el optimizador R8 , el compilador que controla el código no utilizado y la eliminación de recursos para la reducción, el cambio de nombre o la minificación del código, y la optimización de la app.
Habilitar R8 es un paso fundamental para preparar una app para su lanzamiento, pero requiere que los desarrolladores proporcionen orientación en forma de "reglas de conservación".
Después de leer este artículo, mira el video de la Semana de enfoque en el rendimiento sobre cómo habilitar, depurar y solucionar problemas del optimizador R8 en YouTube.
Por qué se necesitan las reglas de conservación
La necesidad de escribir reglas de conservación surge de un conflicto central: R8 es una herramienta de análisis estático, pero las apps para Android suelen depender de patrones de ejecución dinámicos, como la reflexión o las llamadas dentro y fuera del código nativo con la JNI (interfaz nativa de Java).
R8 compila 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 predecir eso, por lo que identificará ese código como sin usar y lo quitará, lo que provocará fallas en el tiempo de ejecución.
Una regla de conservación es una instrucción explícita para el compilador de R8 que indica lo siguiente: "Esta clase, este método o este campo específicos son un punto de entrada al que se accederá de forma dinámica en el tiempo de ejecución. Debes conservarla, incluso si no encuentras una referencia directa a ella".
Consulta la guía oficial para obtener más detalles sobre las reglas de conservación.
Dónde escribir las reglas de Keep
Las reglas de conservación personalizadas para 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 app o de la biblioteca. Luego, 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",
)
}
Usa el archivo predeterminado correcto
El método getDefaultProguardFile importa un conjunto predeterminado de reglas que proporciona el SDK de Android. Si usas el archivo incorrecto, es posible que tu app no esté optimizada. Asegúrate de usar proguard-android-optimize.txt. Este archivo proporciona las reglas de conservación predeterminadas para los componentes estándar de Android y habilita las optimizaciones de código de R8. El proguard-android.txt desactualizado solo proporciona las reglas de conservación, pero no habilita las optimizaciones de R8.
Dado que se trata de un problema grave de rendimiento, comenzaremos a advertir a los desarrolladores sobre el uso del archivo incorrecto a partir de la actualización de funciones de Android Studio Narwhal 3. A partir de la versión 9.0 del complemento de Android para Gradle, ya no admitimos el archivo proguard-android.txt obsoleto. Por lo tanto, asegúrate de actualizar a la versión optimizada.
Cómo escribir reglas de conservación
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 correlacionar
Para conocer la sintaxis completa y ver ejemplos, consulta la guía para agregar reglas de conservación.
Antipatrones de la regla de conservación
Es importante conocer las prácticas recomendadas, pero también los antipatrones. Estos antipatrones suelen surgir de malentendidos o atajos para solucionar problemas, y pueden ser catastróficos para el rendimiento de una compilación de producción.
Opciones globales
Estas marcas son activadores globales que nunca se deben usar en una compilación de lanzamiento. Solo se usan para la depuración temporal con el objetivo de aislar un problema.
Usar -dontotptimize de forma eficaz inhabilita las optimizaciones de rendimiento de R8, lo que genera una app más lenta.
Cuando usas -dontobfuscate, inhabilitas todos los cambios de nombre y, cuando usas -dontshrink, se desactiva la eliminación de código no utilizado. Ambas reglas globales aumentan el tamaño de la app.
Evita usar estos parámetros globales en un entorno de producción siempre que sea posible para brindar una experiencia del usuario de la app más eficiente.
Reglas de conservación demasiado generales
La forma más fácil de anular los beneficios de R8 es escribir reglas de Keep demasiado amplias. Las reglas de conservación, como la que se muestra a continuación, indican al optimizador de R8 que no reduzca, ofusque ni optimice ninguna clase de este paquete ni ninguno de sus subpaquetes. Esto quita por completo los beneficios de R8 para todo el paquete. En su lugar, intenta escribir reglas de conservación específicas y detalladas.
-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
Podrías pensar que esta regla significa "no conserves las clases encom.example.package", pero, en realidad, significa "conserva cada clase, método y propiedad en toda la aplicación que no esté en com.example.package". Si esto te sorprendió, te recomendamos que verifiques si hay negaciones en tu configuración de R8.
Reglas redundantes para componentes de Android
Otro error común es agregar manualmente reglas de Keep para los elementos Activities, Services o BroadcastReceivers de tu app. Esto es innecesario. El archivo proguard-android-optimize.txt predeterminado ya incluye las reglas pertinentes para que estos componentes estándar de Android funcionen de inmediato.
Además, muchas bibliotecas incluyen sus propias reglas de Keep. Por lo tanto, no deberías tener que escribir tus propias reglas para estos casos. En caso de que haya un problema con las reglas de conservación de una biblioteca que estés usando, lo mejor es que te comuniques con el autor de la biblioteca para ver cuál es el problema.
Prácticas recomendadas para las reglas de conservación
Ahora que sabes lo que no debes hacer, hablemos de las prácticas recomendadas.
Escribe reglas de conservación específicas
Las buenas reglas de conservación deben ser lo más reducidas y específicas posibles. Solo deben conservar lo que sea necesario, lo que permite que R8 optimice todo lo demás.
| Regla | Calidad |
|---|---|
| Baja: Conserva un paquete completo y sus subpaquetes. |
| Baja: Conserva una clase completa que probablemente siga siendo demasiado amplia. |
| Alta: Solo se conservan los métodos y las propiedades pertinentes de una clase específica. |
Usa ancestros comunes
En lugar de escribir reglas de conservación separadas para varios modelos de datos diferentes, escribe una regla que se dirija a una interfaz o clase base común. La siguiente regla le indica a R8 que conserve cualquier miembro de las clases que implementen esta interfaz y es altamente escalable.
# Keep all fields of any class that implements SerializableModel
-keepclassmembers class * implements com.example.models.SerializableModel {
<fields>;
}
Cómo usar anotaciones para segmentar varias clases
Crea una anotación personalizada (p.ej., @Serialize) y úsala para "etiquetar" las clases que necesitan que se conserven sus campos. Este es otro patrón limpio, declarativo y altamente escalable. También puedes crear reglas de conservación para las anotaciones existentes de los frameworks que usas.
# Keep all fields of any class annotated with @Serialize
-keepclassmembers class * {
@com.example.annotations.Serialize <fields>;
}
Elige la opción de conservación adecuada
La opción de conservación es la parte más importante de la regla. Elegir la opción incorrecta puede inhabilitar la optimización sin necesidad.
| Keep Option | Qué hace |
-keep | Evita que se quiten o cambien de nombre la clase y los miembros mencionados en la declaración . |
-keepclassmembers | Evita que se quiten o cambien de nombre los miembros especificados, pero permite que se quite la clase en sí, aunque solo en las clases que no se quiten de otro modo. |
-keepclasseswithmembers | Una combinación: Conserva la clase y sus miembros, solo si todos los miembros especificados están presentes. |
Puedes obtener más información sobre la opción de Keep en nuestra documentación sobre las opciones de Keep.
Permite la optimización con modificadores
Los modificadores como allowshrinking y allowobfuscation relajan una regla -keep amplia, lo que le devuelve la capacidad de optimización a R8. Por ejemplo, si una biblioteca heredada te obliga a usar -keep en una clase completa, es posible que puedas recuperar algo de optimización si permites 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
Agrega opciones globales para una optimización adicional
Además de las reglas de conservación, puedes agregar marcas globales a tu archivo de configuración de R8 para fomentar aún más la optimización.
-repackageclasses es una opción potente que le indica a R8 que mueva todas las clases ofuscadas a un solo paquete. Esto ahorra una cantidad significativa de espacio en el archivo DEX, ya que quita las cadenas de nombres de paquetes redundantes.
-allowaccessmodification permite que R8 amplíe el acceso (p.ej., private a public) para habilitar la inserción en línea más agresiva. Ahora, esta opción está habilitada de forma predeterminada cuando se usa proguard-android-optimize.txt.
Advertencia: Los autores de bibliotecas nunca deben agregar estos parámetros de optimización global a sus reglas de consumidor, ya que se aplicarían de forma forzada a toda la app.
Y, para que sea aún más claro, en la versión 9.0 del complemento de Android para Gradle, comenzaremos a ignorar por completo las marcas de optimización global de las bibliotecas.
Prácticas recomendadas para bibliotecas
Todas las apps para Android dependen de las bibliotecas de una u otra forma. Hablemos 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 de Keep necesarias a sus consumidores. Estas reglas se colocan en un archivo consumer-rules.pro, que luego se incluye automáticamente en el archivo AAR de la biblioteca.
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
...
}
Para los consumidores de la biblioteca
Cómo filtrar reglas de Keep problemáticas
Si debes usar una biblioteca que incluye reglas de conservación problemáticas, puedes filtrarlas en tu archivo build.gradle.kts a partir de AGP 9.0. Esto le indica a R8 que ignore las reglas provenientes 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 estrategia de configuración definitiva de R8 es eliminar por completo la necesidad de escribir reglas de Keep. En muchas apps, esto se puede lograr 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 con mayor facilidad qué código se usa realmente durante el tiempo de ejecución y qué código se puede quitar. Además, no usar ninguna reflexión dinámica significa que no hay puntos de entrada "ocultos" y, por lo tanto, no se necesitan reglas de conservación. Cuando elijas una biblioteca nueva, siempre prefiere una solución que use la generación de código en lugar de la reflexión.
Para obtener más información sobre cómo elegir bibliotecas, consulta Cómo elegir bibliotecas de forma inteligente.
Cómo depurar y solucionar problemas de tu configuración de R8
Cuando R8 quita código que debería haber conservado o tu APK es más grande de lo esperado, usa estas herramientas para diagnosticar el problema.
Cómo encontrar reglas de conservación duplicadas y globales
Dado que R8 combina reglas de docenas de fuentes, puede ser difícil saber cuál es el conjunto de reglas "final". Agregar esta marca a tu archivo proguard-rules.pro genera 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 para encontrar reglas redundantes o rastrear una regla problemática (como -dontoptimize) hasta la biblioteca específica que la incluyó.
Pregúntale a R8: ¿Por qué te quedas con esto?
Si una clase que esperabas que se quitara aún está en tu app, R8 puede decirte por qué. Simplemente agrega 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 hizo que conservara esa clase, lo que te permitirá rastrear la referencia y ajustar tus reglas.
Para obtener una guía completa, consulta la sección Soluciona problemas de R8.
Próximos pasos
R8 es una herramienta potente para mejorar el rendimiento de las apps para Android. Su eficacia depende de una comprensión correcta de su funcionamiento como motor de análisis estático.
Si escribes reglas específicas a nivel de los miembros, aprovechas los elementos superiores y las anotaciones, y eliges cuidadosamente las opciones de conservación adecuadas, puedes conservar exactamente lo que es necesario. La práctica más avanzada es eliminar por completo la necesidad de reglas eligiendo bibliotecas modernas basadas en codegen en lugar de sus predecesoras basadas en la reflexión.
Mientras sigues la Semana de Performance Spotlight, asegúrate de mirar el video de hoy en YouTube y continuar con nuestro desafío R8. Usa #optimizationEnabled para cualquier pregunta sobre cómo habilitar R8 o solucionar problemas relacionados con él. Estamos aquí para ayudarte.
Es hora de que descubras los beneficios por tu cuenta.
Te desafiamos a habilitar el modo completo de R8 para tu app hoy mismo.
- Sigue nuestras guías para desarrolladores y comienza: Habilita la optimización de la app.
-
Comprueba si aún usas
proguard-android.txty reemplázalo porproguard-android-optimize.txt. - Luego, mide el impacto. No solo sientas la diferencia, verifícala. Para medir las mejoras en el rendimiento, adapta el código de nuestra app de ejemplo de Macrobenchmark en GitHub para medir los tiempos de inicio antes y después.
Tenemos la certeza de que notarás una mejora significativa en el rendimiento de tu app.
Mientras lo haces, usa la etiqueta de redes sociales #AskAndroid para enviar tus preguntas. Durante toda la semana, nuestros expertos supervisan y responden tus preguntas.
No te pierdas el contenido de mañana, en el que hablaremos sobre la optimización guiada por el perfil con perfiles de Baseline y de inicio, compartiremos cómo mejoró el rendimiento de la renderización de Compose en las versiones anteriores y explicaremos las consideraciones de rendimiento para el trabajo en segundo plano.
Seguir leyendo
-
Novedades sobre productos
Si eres desarrollador de Android y quieres implementar funciones innovadoras basadas en IA en tu app, hace poco lanzamos nuevas y potentes actualizaciones.
Thomas Ezan • Lectura de 3 min
-
Novedades sobre productos
Android 17 alcanzó la versión beta 4, la última versión beta programada de este ciclo de lanzamiento, un hito fundamental para la compatibilidad de las apps y la estabilidad de la plataforma.
Daniel Galpin • Lectura de 4 min
-
Novedades sobre productos
Hacer de Google Play la experiencia más segura y confiable posible Hoy anunciamos un nuevo conjunto de actualizaciones de políticas y una función de transferencia de cuentas para mejorar la privacidad de los usuarios y proteger tu empresa del fraude.
Bennet Manuel • Lectura de 3 min
Mantente al día
Recibe la información más reciente sobre el desarrollo de Android en tu bandeja de entrada todas las semanas.