Agrega reglas de retención

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() o Method.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 o final.

  • <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>;
}