De forma predeterminada, R8 realiza muchas optimizaciones para mejorar el rendimiento y el tamaño, pero es posible que las optimizaciones no funcionen para tu app de inmediato. Si activas R8 (o habilitas el modo completo) en una app grande por primera vez, intenta adoptar las optimizaciones de forma incremental: inhabilita temporalmente la ofuscación y habilita R8 para partes del código a la vez, en lugar de para todo el código de tu app. Te recomendamos que adoptes este enfoque incremental durante el desarrollo local, pero también puedes usarlo durante las pruebas de QA internas o incluso en producción como un lanzamiento gradual. Los pasos exactos que sigas dependerán del cronograma deseado y de la confianza en la cobertura de las pruebas previas al lanzamiento.
Limita las optimizaciones
R8 realiza muchos tipos de optimizaciones, como quitar código, reescribir código y quitar recursos. A continuación, se incluyen algunas descripciones generales de los tipos de optimización:
- Reducción de código (o tree shaking): Quita el código sin referencias.
- Ofuscación (o minimización de identificadores): Acorta los nombres de las clases y los métodos.
- Optimización: Reescribe el código, por ejemplo, la inserción.
Para reducir la posibilidad de errores, puedes comenzar habilitando solo algunas de estas optimizaciones.
Habilita solo el tree shaking
La reducción de código, también conocida como tree shaking, quita el código que parece no tener referencias. Te recomendamos que comiences solo con el tree shaking, ya que es lo más sencillo.
Para habilitar solo el tree shaking, agrega lo siguiente a tu archivo proguard-rules.pro para desactivar los otros tipos de optimizaciones. Desactivar la ofuscación es clave porque facilita mucho la lectura de los seguimientos de pila.
-dontobfuscate // Use temporarily to turn off identifier minification
-dontoptimize // Use temporarily to turn off optimization
Al final, no querrás enviar esta configuración, ya que limita drásticamente la capacidad de R8 para optimizar el código, pero es un excelente punto de partida cuando adoptas R8 por primera vez en una base de código grande con problemas para corregir.
Usa el modo de compatibilidad
De forma predeterminada, R8 se ejecuta en modo completo. El modo completo proporciona un rendimiento significativamente mejorado y ahorros de tamaño, pero puedes inhabilitarlo temporalmente y usar el modo de compatibilidad en su lugar cuando habilites la minimización por primera vez.
Para usar el modo de compatibilidad, usa el siguiente parámetro de configuración en tu archivo gradle.properties:
android.enableR8.fullMode = false // Use temporarily to disable full mode
Habilita el resto de las optimizaciones
Cuando confirmes que el tree shaking funciona para tu app, puedes quitar la configuración anterior para volver a habilitar la ofuscación, la optimización y el modo completo de R8. Ten en cuenta que la ofuscación puede dificultar la depuración, por lo que te recomendamos que primero abordes los problemas de tree shaking.
Para obtener más información sobre cómo desofuscar seguimientos de pila, consulta Cómo recuperar el seguimiento de pila original.
Limita el alcance de la optimización
Una compilación completamente optimizada optimiza todo el código en cada biblioteca y paquete, por lo que es común encontrar problemas con R8 cuando lo activas por primera vez. Si encuentras un problema con la optimización en una parte de la app, no desactives R8 por completo o perderás los beneficios en todos los demás lugares. En su lugar, inhabilita temporalmente R8 solo en las partes de tu app que causan problemas.
Usa reglas keep en todo el paquete
Te recomendamos que uses reglas keep en todo el paquete como una forma de inhabilitar temporalmente R8 en partes de tu app. Siempre debes volver para corregir estos problemas de optimización más adelante. Por lo general, esta es una solución provisoria para evitar las áreas problemáticas.
Por ejemplo, si parte de tu app usa Gson en gran medida y causa problemas con la
optimización, la solución ideal es agregar reglas keep
más específicas o cambiar a una solución de codegen. Sin embargo, para desbloquear la optimización del resto de la app, puedes colocar el código que define tus tipos de Gson en un subpaquete dedicado y agregar una regla como esta a tu archivo proguard-rules.pro:
-keep class com.myapp.json.** { *; }
Si alguna biblioteca que usas tiene reflexión en componentes internos, puedes agregar una regla keep para toda la biblioteca de manera similar. Deberás inspeccionar el código o el archivo JAR/AAR de la biblioteca para encontrar el paquete adecuado para conservar. Nuevamente, no se recomienda mantener esto a largo plazo, pero puede desbloquear la optimización del resto de la app:
-keep class com.somelibrary.** { *; }
Quita las reglas keep en todo el paquete
Una vez que tu app funcione correctamente con las reglas keep en todo el paquete, debes volver y agregar reglas keep específicas o quitar el uso de la reflexión o la biblioteca que requiere la regla keep en primer lugar.
Por ejemplo, conservar todas las reglas que extienden una clase determinada es muy común en AndroidX para conservar solo las clases pertinentes. Por lo general, la reflexión solo debe segmentar clases o métodos que extiendan ciertas clases abstractas, implementen ciertas interfaces o en clases con una anotación de tiempo de ejecución específica. Cada una de estas son formas compatibles de definir reglas keep para que no necesites reglas keep en todo el paquete en tu app final y completamente optimizada.
Si la reflexión está presente en tu propia base de código, puedes reemplazar las reglas keep en todo el paquete por Jetpack Keep. Las reglas generadas son condicionales: R8 conserva el objetivo de una llamada reflexiva solo si también conserva el código que realiza la llamada. Para migrar un paquete, usa las verificaciones de lint de Jetpack Keep para encontrar y anotar sus llamadas reflexivas y, luego, quitar la regla en todo el paquete.