Cuando habilitas la optimización de la app con la configuración predeterminada, R8 realiza optimizaciones exhaustivas para maximizar los beneficios de rendimiento. R8 realiza modificaciones sustanciales en el código, como cambiar el nombre, mover y quitar métodos y campos de clases. Si esto causa errores, debes especificar qué partes del código no se deben modificar escribiendo reglas de conservación.
R8 puede quitar o modificar incorrectamente el código en las siguientes situaciones:
- Reflexión: Código al que se accede mediante reflexión, por ejemplo, con
Class.forName()
oMethod.invoke()
. Por lo general, R8 no puede indicar a qué clases o métodos se accederá de esta manera. - Serialización: Es posible que las clases o los campos necesarios para la serialización y la deserialización parezcan no estar en uso para R8 (esta es otra forma de reflexión).
- Interfaz nativa de Java (JNI): Son métodos de Java a los que se llama desde el código nativo. R8 no analiza el código nativo para ver qué podría volver a llamar a Java.
En esta página, se explica cómo limitar el alcance de las optimizaciones de R8. Para obtener información sobre cómo personalizar los recursos que se conservan, consulta Cómo agregar reglas de conservación de recursos.
Dónde agregar reglas de retención
Debes agregar tus reglas a un archivo proguard-rules.pro
ubicado en el directorio raíz del módulo (es posible que el archivo ya esté allí, pero si no es así, créelo). Para aplicar las reglas del archivo, debes declararlo en el archivo build.gradle.kts
(o build.gradle
) a nivel del módulo, como se muestra en el siguiente código:
Kotlin
android { buildTypes { release { isMinifyEnabled = true isShrinkResources = true proguardFiles( // Default file with default optimization rules. getDefaultProguardFile("proguard-android-optimize.txt"), // File with your custom rules. "proguard-rules.pro" ) ... } } ... }
Groovy
android { buildTypes { release { minifyEnabled true shrinkResources true proguardFiles( // Default file with default optimization rules. getDefaultProguardFile('proguard-android-optimize.txt'), // File with your custom rules. 'proguard-rules.pro' ) } } // ... }
De forma predeterminada, tu secuencia de comandos de compilación también incluye el archivo proguard-android-optimize.txt
. Este archivo incluye reglas que son obligatorias para la mayoría de los proyectos de Android, por lo que debes conservarlo en la secuencia de comandos de compilación.
Cómo escribir reglas de retención
Durante la compilación de la app, R8 detecta qué código se debe conservar en una app a través del análisis del gráfico de llamadas de la app, que comienza en las entradas del manifiesto (como tus actividades o servicios) y rastrea cada llamada a la función de la app y de la biblioteca. R8 quita el código al que no se hace referencia directamente de esta manera, lo que puede causar problemas si el código ejecutado no forma parte de este gráfico, por ejemplo, el código invocado por reflexión. Si escribes tus propias reglas de conservación, puedes informar a R8 sobre el código que debe permanecer en la app.
Para agregar una regla de retención, agrega una línea -keep
en el archivo proguard-rules.pro
.
Cómo mantener la sintaxis de las reglas
Por lo general, las reglas de Keep siguen este formato:
-<KeepOption> [OptionalModifier,...] <ClassSpecification> [{ OptionalMemberSpecification }]
Por ejemplo, para preservar una clase específica y todos sus miembros, usa lo siguiente:
-keep class com.myapp.MyClass { *; }
Para obtener más ejemplos, consulta la sección de ejemplos.
Esto es lo que hacen los componentes de la regla de retención:
<KeepOption>
te permite especificar qué aspectos de una clase se deben conservar:Opción Mantener Descripción -keep
Conserva la clase y los miembros que se enumeran en
[{ OptionalMemberSpecification }]
.-keepclassmembers
Permite la optimización de la clase. Si se conserva la clase, conserva los miembros que se enumeran en
[{ OptionalMemberSpecification }]
.-keepnames
Permite quitar la clase y los miembros, pero no los ocultes ni los modifiques de otras maneras.
-keepclassmembernames
Permite quitar la clase y los miembros, pero no ofusques ni modifiques a los miembros de otras maneras.
-keepclasseswithmembers
No se quitan ni ofuscan las clases si los miembros coinciden con el patrón especificado.
Te recomendamos que uses principalmente
-keepclassmembers
, ya que habilita la mayoría de las optimizaciones y, luego,-keepnames
si es necesario.-keep
no permite ninguna optimización, por lo que debes usarlo con moderación.[OptionalModifier],...]
te permite enumerar cero o más modificadores del lenguaje Java de una clase, por ejemplo,public
ofinal
.<ClassSpecification>
te permite especificar a qué clase (o superclase o interfaz implementada) se debe aplicar la regla de retención. En el caso más simple, esta es una clase completamente calificada.[{ OptionalMemberSpecification }]
te permite filtrar el comportamiento de retención para que solo se incluyan las clases y los métodos que coincidan con ciertos patrones. Por lo general, esto se recomienda en la mayoría de las reglas de retención para evitar conservar más de lo previsto.
Ejemplos de reglas de Keep
Estos son algunos ejemplos de reglas de retención bien diseñadas.
Crea una subclase
Esta regla de retención se empaqueta dentro de androidx.room:room-runtime
para mantener a los constructores de la base de datos creados a partir de la reflexión.
-keep class * extends androidx.room.RoomDatabase { void <init>(); }
Reflexión del framework de Android ObjectAnimator
Esta regla de retención se empaqueta dentro de androidx.vectordrawable:vectordrawable-animated
para permitir que ObjectAnimator
llame a getters o seters desde el código nativo con JNI. Ten en cuenta que los sistemas de animación más nuevos en Compose no requieren reglas de retención como esta, es solo un ejemplo de la estructura de reglas.
-keepclassmembers class androidx.vectordrawable.graphics.drawable.VectorDrawableCompat$* {
void set*(***);
*** get*();
}
Registro de JNI
Esta regla de retención se empaqueta dentro de androidx.graphics:graphics-path
, que se usa para conservar los métodos nativos. Esto puede ser necesario si tu biblioteca registra métodos nativos de forma manual con env->RegisterNatives()
.
-keepclasseswithmembers class androidx.graphics.path.** {
native <methods>;
}