Configura y soluciona problemas de las reglas de conservación de R8
Lectura de 7 min
En el desarrollo moderno de Android, la entrega de 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 R8 optimizador, el compilador que controla el código inactivo 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 la optimización del rendimiento en YouTube para habilitar, depurar y solucionar problemas del optimizador R8.
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 (Java Native Interface).
R8 compila un gráfico del código usado mediante el análisis de llamadas directas. Cuando se accede al código de forma dinámica, el análisis estático de R8 no puede predecir eso, lo identificará como no usado 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 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, incluso si no puedes encontrar una referencia directa a él".
Consulta la guía oficial para obtener más detalles sobre las reglas de conservación.
Dónde escribir reglas de conservación
Las reglas de conservación personalizadas para una aplicación se escriben en un archivo de texto. Por convención, este archivo se denomina 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 de reglas predeterminado 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 archivo proguard-android.txt obsoleto solo proporciona las reglas de conservación, pero no habilita las optimizaciones de R8.
Dado que se trata de un problema de rendimiento grave, comenzaremos a advertir a los desarrolladores sobre el uso del archivo incorrecto, a partir de la versión 3 de Android Studio Narwhal.Además, a partir de la versión 9.0 del complemento de Android para Gradle, ya no admitiremos 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 debe coincidir
Para obtener la sintaxis y los ejemplos completos, consulta la guía para agregar reglas de conservación.
Antipatrones de reglas de conservación
Es importante conocer las prácticas recomendadas, pero también los antipatrones. Estos antipatrones suelen surgir de malentendidos o atajos para la solución de problemas y pueden ser catastróficos para el rendimiento de una compilación de producción.
Opciones globales
Estas marcas son conmutadores globales que nunca se deben usar en una compilación de lanzamiento. Solo se usan para la depuración temporal para aislar un problema.
El uso de -dontotptimize inhabilita de manera efectiva las optimizaciones de rendimiento de R8, lo que hace que la app sea más lenta.
Cuando usas -dontobfuscate, inhabilitas todos los cambios de nombre, y cuando usas -dontshrink, se desactiva la eliminación de código inactivo. Ambas reglas globales aumentan el tamaño de la app.
Evita usar estas marcas globales en un entorno de producción siempre que sea posible para obtener una experiencia del usuario de la app más eficiente.
Reglas de conservación demasiado amplias
La forma más fácil de anular los beneficios de R8 es escribir reglas de conservación demasiado amplias. Las reglas de conservación como la que se muestra a continuación indican al optimizador R8 que no reduzca, no ofusque ni optimice ninguna clase en este paquete ni ninguno de sus subpaquetes. Esto quita por completo los beneficios de R8 para todo ese paquete. En su lugar, intenta escribir reglas de conservación específicas y detalladas.
-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMSEl operador de inversión (!)
El operador de inversión (!) parece una forma eficaz de excluir un paquete de una regla. Pero no es tan simple. Veamos este ejemplo.
-keep class !com.example.my_package.** { *; } // USE WITH CAUTIONEs posible que creas que esta regla significa "no conservar las clases encom.example.package." Pero, en realidad, significa "conservar todas las clases, los métodos y las propiedades de toda la aplicación que no estén 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 conservación para las Activities, los Services o los 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 conservación. Por lo tanto, no deberías tener que escribir tus propias reglas para ellas. En caso de que haya un problema con las reglas de conservación de una biblioteca que estás usando, lo mejor es comunicarte con el autor de la biblioteca para ver cuál es el problema.
Prácticas recomendadas para las reglas de conservación
Ahora que ya sabes qué no hacer, hablemos de las prácticas recomendadas.
Escribe reglas de conservación detalladas
Las buenas reglas de conservación deben ser lo más detalladas y específicas posible. Solo deben conservar lo que es 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 sea demasiado amplia. |
-keepclassmembers class com.example.MyClass {
private java.lang.String secretMessage;
public void onNativeEvent(java.lang.String);
} | 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 oriente a una interfaz o clase base común. La siguiente regla le indica a R8 que conserve cualquier miembro de las clases que implementan esta interfaz y es altamente escalable.
# Keep all fields of any class that implements SerializableModel
-keepclassmembers class * implements com.example.models.SerializableModel {
<fields>;
}Usa anotaciones para orientar 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 anotaciones ya existentes de los frameworks que estás usando.
# Keep all fields of any class annotated with @Serialize
-keepclassmembers class * {
@com.example.annotations.Serialize <fields>;
}Elige la opción de conservación correcta
La opción de conservación es la parte más importante de la regla. Si eliges la incorrecta, puedes inhabilitar la optimización sin necesidad.
| Opción de conservación | Qué hace |
-keep | Evita que se quiten o se cambien de nombre la clase y los miembros mencionados en la declaración . |
-keepclassmembers | Evita que se quiten o se cambien de nombre los miembros especificados , pero permite que se quite la clase en sí, pero solo en las clases que no se quitan de otra manera. |
-keepclasseswithmembers | Es una combinación: Conserva la clase y sus miembros, solo si están presentes todos los miembros especificados. |
Puedes obtener más información sobre la opción de conservación en nuestra documentación para las opciones de conservación.
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 espacio significativo en el archivo DEX, ya que quita las cadenas de nombres de paquetes redundantes.
-allowaccessmodification permite que R8 amplíe el acceso (p.ej., de private a public) para habilitar la inserción más agresiva. Ahora, esto está habilitado de forma predeterminada cuando se usa proguard-android-optimize.txt.
Advertencia: Los autores de bibliotecas nunca deben agregar estas marcas de optimización global a sus reglas de consumidor, ya que se aplicarían de forma forzosa a toda la app.
Y para que quede 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 forma o de otra. Por lo tanto, 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 conservación necesarias a sus consumidores. Estas reglas se colocan en un archivo consumer-rules.pro, que luego se agrupa automáticamente dentro del archivo AAR de la biblioteca.
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
...
}Para consumidores de bibliotecas
Filtra las reglas de conservación 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 que provienen 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 de R8 definitiva es quitar por completo la necesidad de escribir reglas de conservación. Para 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 en 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 una biblioteca de forma inteligente.
Depura y soluciona problemas de tu configuración de R8
Cuando R8 quita el código que debería haber conservado o tu APK es más grande de lo esperado, usa estas herramientas para diagnosticar el problema.
Busca reglas de conservación duplicadas y globales
Debido a que R8 combina reglas de docenas de fuentes, puede ser difícil saber cuál es el conjunto de reglas "final". Si agregas esta marca a tu archivo proguard-rules.pro, se 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 reglas redundantes o rastrear una regla problemática (como -dontoptimize) hasta la biblioteca específica que la incluyó.
Pregúntale a R8: ¿Por qué conservas esto?
Si una clase que esperabas que se quitara aún está en tu app, R8 puede decirte por qué. Solo 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 miembro, aprovechas los ancestros y las anotaciones, y eliges cuidadosamente las opciones de conservación correctas, 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 reflexión.
Mientras sigues la Semana de la optimización del rendimiento, asegúrate de mirar el video de la Semana de la optimización del rendimiento de hoy en YouTube y continuar con nuestro desafío de R8. Usa #optimizationEnabled para cualquier pregunta sobre cómo habilitar o solucionar problemas de R8. Estamos aquí para ayudarte.
Es hora de que veas los beneficios por ti mismo.
Te desafiamos a habilitar el modo completo de R8 para tu app hoy.
- Sigue nuestras guías para desarrolladores para comenzar: Habilita la optimización de la app.
- Verifica si aún usas
proguard-android.txty reemplázalo porproguard-android-optimize.txt. - Luego, mide el impacto. No solo sientas la diferencia, verifícala. Mide tus ganancias de rendimiento adaptando el código de nuestra app de ejemplo de Macrobenchmark en GitHub para medir tus tiempos de inicio antes y después.
Estamos seguros de que verás una mejora significativa en el rendimiento de tu app.
Mientras lo haces, usa la etiqueta social #AskAndroid para hacer 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 perfiles con perfiles de Baseline y de inicio, compartiremos cómo mejoró el rendimiento de la renderización de Compose en las versiones anteriores y compartiremos consideraciones de rendimiento para el trabajo en segundo plano.
-
Novedades de productosEn Google Play, nos comprometemos a brindar la mejor experiencia posible a los usuarios y, al mismo tiempo, garantizar que los desarrolladores tengan las herramientas y la adaptabilidad necesarias para tener éxito.
Paul Feng • Lectura de 3 min -
Novedades de productosEl año pasado, presentamos la verificación de desarrolladores de Android para fortalecer la seguridad del ecosistema y evitar que los agentes maliciosos se oculten detrás del anonimato para lanzar apps dañinas.
Matthew Forsythe • Lectura de 2 min -
Novedades de productosDesde superposiciones aumentadas hasta entornos completamente envolventes, el ecosistema de Android XR se expande rápidamente, y el Samsung Galaxy XR ya está disponible.
Stevan Silva, Vinny DaSilva • Lectura de 3 min
Recibe la información más reciente sobre el desarrollo de Android en tu bandeja de entrada todas las semanas.