Únete a ⁠ #Android11: The Beta Launch Show el 3 de junio.

Cómo reducir, ofuscar y optimizar tu app

Para que tu app sea lo más pequeña posible, debes habilitar la reducción en tu compilación de lanzamiento a fin de quitar el código y los recursos que no se usan. Cuando habilitas esta opción, también te beneficias de la ofuscación, que acorta los nombres de las clases y los miembros de tu app, y la optimización, que aplica estrategias más agresivas a fin de reducir aún más el tamaño de tu app. En esta página, se describe cómo R8 realiza estas tareas en tiempo de compilación en tu proyecto y cómo puedes personalizarlas.

Cuando compilas tu proyecto mediante el complemento de Gradle para Android 3.4.0 o una versión posterior, el complemento ya no usa ProGuard a fin de realizar la optimización del código en tiempo de compilación. En cambio, el complemento trabaja con el compilador R8 para administrar las siguientes tareas en tiempo de compilación:

  • Reducción de código (o eliminación de código obsoleto): Detecta y quita de forma segura de tu app las clases, los campos, los métodos y los atributos que no se usan, y sus dependencias de biblioteca (lo que la convierte en una herramienta útil para evitar sobrepasar el límite de referencia de 64,000). Por ejemplo, si usas solo unas pocas API de una dependencia de biblioteca, la reducción puede identificar y quitar el código de biblioteca que tu app no usa. Para obtener más información, ve a la sección sobre cómo reducir tu código.
  • Reducción de recursos: Quita los recursos que no se usan de tu app empaquetada, incluidos los que no se usan en las dependencias de la biblioteca de tu app. Funciona junto con la reducción de código, de modo que cuando se quite el código que no se usa, también se puedan quitar de forma segura los recursos a los que ya no se haga referencia. Para obtener más información, consulta la sección sobre cómo reducir tus recursos.
  • Ofuscación: Acorta el nombre de las clases y los miembros, lo que genera archivos DEX de menor tamaño. Para obtener más información, consulta la sección sobre cómo ofuscar tu código.
  • Optimización: Inspecciona y vuelve a escribir el código para reducir aún más el tamaño de los archivos DEX de tu app. Por ejemplo, si R8 detecta que nunca se toma la rama else {} para una declaración "if/else" determinada, quita el código para esa rama else {}. Para obtener más información, consulta la sección sobre optimización de código.

Al crear la versión de lanzamiento de tu app, R8 ejecuta las tareas en tiempo de compilación descritas anteriormente de forma predeterminada y automática. Sin embargo, puedes inhabilitar ciertas tareas o personalizar el comportamiento de R8 por medio de los archivos de reglas ProGuard. De hecho, R8 funciona con todos tus archivos de reglas ProGuard existentes, por lo que no es necesario que cambies las reglas existentes si deseas actualizar el complemento de Gradle para Android a fin de que use R8.

Cómo habilitar la reducción, la ofuscación y la optimización

Cuando usas Android Studio 3.4 o el complemento de Gradle para Android 3.4.0 y versiones posteriores, R8 es el compilador predeterminado que convierte el código de bytes Java de tu proyecto al formato DEX, que se ejecuta en la plataforma de Android. Sin embargo, cuando creas un nuevo proyecto con Android Studio, las opciones de reducción, ofuscación y optimización de código no están habilitadas de manera predeterminada, ya que estas optimizaciones en tiempo de compilación aumentan el tiempo que tarda en compilarse tu proyecto y pueden introducir errores si no personalizas correctamente el código que se conservará.

Por lo tanto, es mejor que habilites estas tareas en tiempo de compilación cuando compiles la versión final de la app que probarás antes de publicar. Para habilitar la reducción, la ofuscación y la optimización, incluye lo siguiente en tu archivo build.gradle en el nivel del proyecto.

android {
        buildTypes {
            release {
                // Enables code shrinking, obfuscation, and optimization for only
                // your project's release build type.
                minifyEnabled true

                // Enables resource shrinking, which is performed by the
                // Android Gradle plugin.
                shrinkResources true

                // Includes the default ProGuard rules files that are packaged with
                // the Android Gradle plugin. To learn more, go to the section about
                // R8 configuration files.
                proguardFiles getDefaultProguardFile(
                        'proguard-android-optimize.txt'),
                        'proguard-rules.pro'
            }
        }
        ...
    }
    

Archivos de configuración de R8

R8 usa archivos de reglas ProGuard para modificar su comportamiento predeterminado y comprender mejor la estructura de tu app, como las clases que actúan como puntos de entrada al código de tu app. Si bien puedes modificar algunos de los archivos de reglas, es posible que algunas reglas se generen automáticamente por medio de las herramientas de tiempo de compilación, como AAPT2, o se hereden de las dependencias de la biblioteca de tu app. En la siguiente tabla, se describen las fuentes de los archivos de reglas ProGuard que usa R8.

Fuente Ubicación Descripción
Android Studio <module-dir>/proguard-rules.pro Cuando creas un módulo nuevo con Android Studio, el IDE crea un archivo proguard-rules.pro en el directorio raíz de ese módulo.

De manera predeterminada, este archivo no aplica ninguna regla. Por lo tanto, deberás incluir aquí tus propias reglas ProGuard, como tus reglas de conservación personalizadas.

Complemento de Gradle para Android El complemento de Gradle para Android lo genera en el momento de la compilación. El complemento de Gradle para Android genera el archivo proguard-android-optimize.txt, el cual incluye reglas que son útiles para la mayoría de los proyectos de Android y habilita las anotaciones @Keep*.

De forma predeterminada, al crear un módulo nuevo con Android Studio, el archivo build.gradle de nivel de módulo incluye por ti este archivo de reglas en la versión.

Nota: El complemento de Gradle para Android incluye otros archivos de reglas ProGuard predefinidos, pero te recomendamos que uses proguard-android-optimize.txt.

Dependencias de bibliotecas Bibliotecas AAR: <library-dir>/proguard.txt

Bibliotecas JAR: <library-dir>/META-INF/proguard/

Si una biblioteca AAR se publica con su propio archivo de reglas ProGuard y además incluyes ese AAR como una dependencia en tiempo de compilación, R8 aplicará automáticamente sus reglas al compilar tu proyecto.

El uso de archivos de reglas que se empaquetan con bibliotecas AAR es útil si se requieren ciertas reglas de conservación para que la biblioteca funcione correctamente (es decir, si el desarrollador de la biblioteca realizó por ti los pasos para la solución de problemas).

Sin embargo, debes tener en cuenta que, como las reglas ProGuard son aditivas, no se podrán quitar las que incluyan una dependencia de la biblioteca AAR y es posible que afecten la compilación de otras partes de tu app. Por ejemplo, si una biblioteca incluye una regla para inhabilitar las optimizaciones de código, esta inhabilitará las optimizaciones de todo tu proyecto.

Android Asset Package Tool 2 (AAPT2) Después de compilar tu proyecto con minifyEnabled true: <module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt AAPT2 genera reglas de conservación que se basan en las referencias a las clases del manifiesto de tu app, a los diseños y a otros recursos de app. Por ejemplo, AAPT2 incluye una regla de conservación para cada Actividad que registras como punto de entrada en el manifiesto de tu app.
Archivos de configuración personalizados De manera predeterminada, cuando creas un nuevo módulo con Android Studio, el IDE crea el archivo <module-dir>/proguard-rules.pro para que agregues tus propias reglas. Puedes incluir otras opciones de configuración y R8 las aplicará en el momento de la compilación.

Cuando configuras la propiedad minifyEnabled en true, R8 combina reglas de todas las fuentes disponibles que se mencionaron anteriormente. Es importante que lo recuerdes cuando soluciones problemas con R8, ya que otras dependencias en tiempo de compilación (como las dependencias de la biblioteca) podrían introducir cambios en el comportamiento de R8 que desconoces.

Para generar un informe completo sobre todas las reglas que R8 aplica al compilar tu proyecto, incluye lo siguiente en el archivo proguard-rules.pro de tu módulo:

// You can specify any path and filename.
    -printconfiguration ~/tmp/full-r8-config.txt
    

Cómo incluir opciones de configuración adicionales

Cuando creas un nuevo proyecto o módulo con Android Studio, el IDE crea un archivo <module-dir>/proguard-rules.pro para que incluyas tus propias reglas. También puedes incluir reglas adicionales de otros archivos agregándolas a la propiedad proguardFiles en el archivo build.gradle de tu módulo.

Por ejemplo, puedes agregar reglas específicas de cada variante de compilación incluyendo otra propiedad proguardFiles en el bloque productFlavor correspondiente. En el siguiente archivo de Gradle, se agrega flavor2-rules.pro a la variante de producto flavor2. Ahora, flavor2 usa las tres reglas de ProGuard, debido a que también se aplican las reglas del bloque release.

android {
        ...
        buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile(
                  'proguard-android-optimize.txt'),
                  // List additional ProGuard rules for the given build type here. By default,
                  // Android Studio creates and includes an empty rules file for you (located
                  // at the root directory of each module).
                  'proguard-rules.pro'
            }
        }
        flavorDimensions "version"
        productFlavors {
            flavor1 {
              ...
            }
            flavor2 {
                proguardFile 'flavor2-rules.pro'
            }
        }
    }
    

Cómo reducir tu código

La reducción de código con R8 se habilita de forma predeterminada cuando configuras la propiedad minifyEnabled en true.

La reducción de código (también conocida como eliminación de código muerto) es el proceso de eliminación de código que R8 determina que no es necesario en el tiempo de ejecución. Este proceso puede reducir en gran medida el tamaño de tu app si, por ejemplo, esta incluye muchas dependencias de biblioteca, pero usa solo una pequeña parte de su funcionalidad.

Para reducir el código de tu app, R8 primero determina todos los puntos de entrada al código de tu app en función de la combinación del conjunto de archivos de configuración. Estos puntos de entrada incluyen todas las clases que la plataforma de Android puede usar para abrir las actividades o servicios de tu app. R8 inspecciona el código de tu app a partir de cada punto de entrada a fin de compilar un gráfico de todos los métodos, variables de miembro y otras clases a las que tu app podría acceder en el tiempo de ejecución. El código que no está conectado a ese gráfico se considera inaccesible y puedes quitarlo de tu app.

En la Figura 1, se muestra una app con una dependencia de biblioteca en tiempo de ejecución. Cuando inspecciona el código de la app, R8 determina que es posible acceder a los métodos foo(), faz() y bar() desde el punto de entrada MainActivity.class. Sin embargo, tu app nunca usa la clase OkayApi.class o su método baz() en el tiempo de ejecución; por lo tanto, R8 quitará ese código cuando reduzca tu app.

Figura 1: En el tiempo de compilación, R8 compila un gráfico basado en las reglas de conservación combinadas de tu proyecto para determinar el código inaccesible.

R8 determina los puntos de entrada por medio de reglas -keep en los archivos de configuración de R8 del proyecto. Es decir, las reglas de conservación especifican las clases que R8 no debe descartar al reducir tu app, y R8 las considera como posibles puntos de entrada a tu app. El complemento de Gradle para Android y AAPT2 generan automáticamente las reglas de conservación que requiere la mayoría de los proyectos de apps, como las actividades, las vistas y los servicios de tu app. Sin embargo, si necesitas personalizar este comportamiento predeterminado con reglas de conservación adicionales, lee la sección sobre cómo determinar de forma personalizada el código que se conservará.

En cambio, si solo quieres reducir el tamaño de los recursos de tu app, ve a la sección sobre cómo reducir tus recursos.

Cómo personalizar el código que se conservará

En la mayoría de las situaciones, el archivo predeterminado de reglas ProGuard (proguard-android- optimize.txt) es suficiente para que R8 quite únicamente el código que no se usa. Sin embargo, R8 tiene dificultades para analizar algunas situaciones y es posible que quite código que tu app sí necesita. Los siguientes son ejemplos de situaciones en las cuales este podría quitar código de forma incorrecta:

  • Cuando tu app llama a un método desde la Interfaz nativa Java (JNI)
  • Cuando tu app busca código en el tiempo de ejecución (como con la reflexión)

Cuando pruebes tu app, deberías ver los errores que se produjeron por la eliminación incorrecta de código, pero también puedes generar un informe sobre el código quitado para revisar qué código se quitó.

Para corregir los errores y forzar a R8 a que conserve cierto código, agrega una línea -keep en el archivo de reglas ProGuard. Por ejemplo:

-keep public class MyClass
    

También puedes agregar la anotación @Keep al código que desees conservar. Si se agrega una anotación @Keep a una clase, se mantendrá toda la clase tal como esté. Si se agrega a un método o campo, se mantendrán intactos tanto el campo o método (y su nombre) como el nombre de la clase. Ten en cuenta que esta anotación solo está disponible cuando se usa la Biblioteca de anotaciones de AndroidX y además incluyes el archivo de reglas ProGuard que se empaqueta con el complemento de Gradle para Android, como se describe en la sección sobre cómo habilitar la reducción.

Existen muchas consideraciones que debes tener en cuenta al usar la opción -keep. Para obtener más información sobre cómo personalizar tu archivo de reglas, lee el Manual de ProGuard. En la sección Solución de problemas, se describen otros problemas comunes que podrías encontrar cuando reduzcas tu código.

Cómo reducir tus recursos

La reducción de recursos funciona únicamente junto con la reducción de código. Una vez que el reductor de código quita todo el código que no se usa, el reductor de recursos puede identificar los recursos que la app todavía usa, en especial cuando agregas bibliotecas de código que los incluyen. En este caso, deberás quitar el código de biblioteca que no se usa de modo que deje de hacerse referencia a los recursos de biblioteca y el reductor de recursos pueda quitarlos.

Para habilitar la reducción de recursos, establece la propiedad shrinkResources como true en el archivo build.gradle (junto con minifyEnabled para la reducción de código). Por ejemplo:

android {
        ...
        buildTypes {
            release {
                shrinkResources true
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android.txt'),
                        'proguard-rules.pro'
            }
        }
    }
    

Si todavía no compilaste tu app usando minifyEnabled para la reducción del código, prueba lo anterior antes de habilitar shrinkResources, ya que probablemente debas modificar tu archivo proguard-rules.pro para que mantenga las clases o métodos que creaste o invocaste de manera dinámica antes de comenzar a quitar recursos.

Cómo personalizar los recursos que se conservarán

Si deseas conservar o descartar algún recurso específico, crea un archivo XML en tu proyecto con una etiqueta <resources> y especifica los recursos que desees conservar en el atributo tools:keep y los que desees descartar en el atributo tools:discard. Ambos atributos aceptan una lista de nombres de recursos separados por comas. Puedes usar el carácter de asterisco como comodín.

Por ejemplo:

    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:tools="http://schemas.android.com/tools"
        tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
        tools:discard="@layout/unused2" />
    

Guarda este archivo en los recursos de tu proyecto; por ejemplo, en res/raw/keep.xml. Durante la compilación, este archivo no se empaquetará en tu APK.

Especificar los recursos que deben descartarse quizá te parezca absurdo, ya que puedes simplemente borrarlos, pero puede resultar útil cuando usas variantes de compilación. Por ejemplo, puedes ubicar todos tus recursos en el directorio común del proyecto y crear un archivo keep.xml diferente para cada variante de compilación cuando sabes que es posible que se use un recurso determinado en el código (y, por lo tanto, el reductor no lo quitará), pero que no se usará para la variante de compilación determinada. También podría ocurrir que las herramientas de compilación hayan identificado incorrectamente un recurso como necesario, lo que podría suceder porque el compilador agrega los ID de recursos intercalados y, por lo tanto, es posible que el analizador de recursos no pueda diferenciar un recurso de un valor de número entero al que se hace referencia de manera genuina en el código que por casualidad tiene el mismo valor.

Cómo habilitar comprobaciones de referencia estrictas

Por lo general, el reductor de recursos puede determinar de manera precisa si un recurso se usa o no. Sin embargo, si tu código llama a la clase Resources.getIdentifier(), o si alguna de tus bibliotecas lo hace (esto sucede en el caso de la biblioteca AppCompat), significa que busca nombres de recursos basados en strings generadas de manera dinámica. Cuando lo haces, el reductor de recursos adopta un comportamiento defensivo de forma predeterminada y marca todos los recursos cuyos formatos de nombre coincidan como que están potencialmente en uso y no pueden quitarse.

El siguiente código, por ejemplo, hace que todos los recursos con el prefijo img_ se marquen como usados.

Kotlin

    val name = String.format("img_%1d", angle + 1)
    val res = resources.getIdentifier(name, "drawable", packageName)
    

Java

    String name = String.format("img_%1d", angle + 1);
    res = getResources().getIdentifier(name, "drawable", getPackageName());
    

El reductor de recursos también analiza todas las constantes de strings de tu código, así como los diferentes recursos res/raw/, y busca las URL de los recursos en un formato similar a file:///android_res/drawable//ic_plus_anim_016.png. Si encuentra strings como esta u otras que, al parecer, podrían usarse para construir URL como esta, no las quita.

Estos son ejemplos del modo de reducción seguro que se habilita de forma predeterminada. Sin embargo, puedes descartar esta estrategia "más vale prevenir que lamentar" y especificar que el reductor de recursos únicamente conserve los recursos que esté seguro de que se usan. Para hacerlo, fija shrinkMode en strict en el archivo keep.xml, como se indica a continuación:

    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:tools="http://schemas.android.com/tools"
        tools:shrinkMode="strict" />
    

Si habilitas el modo de reducción estricta y tu código también hace referencia a recursos con strings generadas de manera dinámica, como se observa más arriba, debes conservar manualmente esos recursos usando el atributo tools:keep.

Cómo quitar recursos alternativos que no se usan

El reductor de recursos de Gradle solo quita recursos a los que no se hace referencia en el código de tu app, lo que significa que no se quitarán los recursos alternativos para las diferentes opciones de configuración del dispositivo. De ser necesario, puedes usar la propiedad resConfigs del complemento de Gradle para Android a fin de quitar archivos de recursos alternativos que tu app no necesite.

Por ejemplo, si usas una biblioteca que incluye recursos de idioma (como AppCompat o los Servicios de Google Play), tu APK incluirá todas las strings de idiomas traducidos para los mensajes en esas bibliotecas, sin importar si el resto de tu app está traducida en los mismos idiomas o no. Si deseas mantener solo los idiomas que tu app admite oficialmente, puedes especificarlos a través de la propiedad resConfig. Se quitarán los recursos para idiomas que no se especifiquen.

En el siguiente fragmento de código, se muestra cómo limitar tus recursos de idioma a inglés y francés únicamente:

android {
        defaultConfig {
            ...
            resConfigs "en", "fr"
        }
    }
    

Del mismo modo, puedes personalizar los recursos de ABI o las densidades de pantalla que quieras incluir en tu APK mediante la compilación de varios APK para que cada uno especifique una configuración del dispositivo diferente.

Cómo fusionar recursos duplicados

De forma predeterminada, Gradle también fusiona los recursos con nombres idénticos, como los elementos de diseño con el mismo nombre que puedan ubicarse en diferentes carpetas de recursos. Este comportamiento no es controlado por la propiedad shrinkResources y no puede inhabilitarse, ya que es necesario evitar errores cuando varios recursos coinciden con el nombre que tu código busca.

La fusión de recursos se produce cuando dos o más archivos comparten un nombre, un tipo y un calificador de recurso idénticos. Gradle selecciona el archivo que considera que es la mejor opción entre los duplicados (en función del orden de prioridad descrito debajo) y transmite solo ese recurso al AAPT para su distribución en el archivo APK.

Gradle busca recursos duplicados en las siguientes ubicaciones:

  • Los recursos principales, asociados con el conjunto de fuentes principales, que generalmente se ubican en src/main/res/.
  • Las superposiciones de variantes, del tipo de compilación y de las variantes de compilación.
  • Las dependencias del proyecto de biblioteca

Gradle fusiona recursos en el siguiente orden de prioridad escalonado:

Dependencias → Principal → Versión de compilación → Tipo de compilación

Por ejemplo, si un recurso duplicado aparece tanto en tus recursos principales como en un tipo de compilación, Gradle seleccionará el que se encuentre en el tipo de compilación.

Si aparecen recursos idénticos en el mismo conjunto de fuentes, Gradle no podrá fusionarlos y emitirá un error de fusión de recursos, Esto puede suceder si defines varios conjuntos de fuentes en la propiedad sourceSet de tu archivo build.gradle; por ejemplo, si tanto src/main/res/ como src/main/res2/ contienen recursos idénticos.

Cómo ocultar tu código

El propósito de la ofuscación es reducir el tamaño de tu app acortando los nombres de las clases, de los métodos y de los campos de tu app. A continuación, se muestra un ejemplo de ofuscación mediante R8:

androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
    androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
        android.content.Context mContext -> a
        int mListItemLayout -> O
        int mViewSpacingRight -> l
        android.widget.Button mButtonNeutral -> w
        int mMultiChoiceItemLayout -> M
        boolean mShowTitle -> P
        int mViewSpacingLeft -> j
        int mButtonPanelSideLayout -> K
    

Si bien la ofuscación no quita el código de tu app, podrás reducir significativamente su tamaño en apps con archivos DEX que indexan varias clases, métodos y campos. Sin embargo, como la ofuscación renombra diferentes partes de tu código, algunas tareas (como la inspección de seguimiento de pila) requieren herramientas adicionales. Para comprender el seguimiento de pila luego de la ofuscación, lee la siguiente sección sobre cómo decodificar un seguimiento de pila ofuscado.

Además, si tu código se basa en una asignación de nombres predictiva para los métodos y las clases de tu app, cuando uses la reflexión, por ejemplo, deberías considerar esas firmas como puntos de entrada y especificar reglas de conservación para ellas, como se describe en la sección acerca de personalizar el código que se conservará. Estas reglas de conservación le indican a R8 que no solo debe conservar ese código en el DEX final de tu app sino también mantener el nombre original.

Cómo decodificar un seguimiento de pila oculto

Una vez que R8 ofusca tu código, podría resultar difícil (si no imposible) comprender el seguimiento de pila porque es probable que se hayan cambiado los nombres de las clases y de los métodos. Además de la capacidad de renombrar, R8 también puede cambiar los números de línea presentes en los seguimientos de pila a fin de reducir aún más el tamaño al escribir los archivos DEX. Por suerte, R8 crea un archivo mapping.txt cada vez que se ejecuta, en el que los nombres de las clases, de los métodos y de los campos ofuscados están asignados a los nombres originales. Este archivo de asignación también contiene información para volver a asignar los números de línea a los números de línea de los archivos fuente originales. R8 guarda el archivo en el directorio <module- name>/build/outputs/mapping/<build-type>/.

Cuando publicas tu app en Google Play, puedes subir el archivo mapping.txt para cada versión de tu APK. Luego, Google Play hará visibles los seguimientos de pila entrantes según los problemas informados por los usuarios para que puedas revisarlos en Google Play Console. Para obtener más información, consulta el artículo del Centro de ayuda sobre cómo desofuscar los seguimientos de pila de fallas.

Para convertir un seguimiento de pila desofuscado en uno que sea legible, usa la secuencia de comandos de ReTrace, que viene incluida con ProGuard.

Optimización de código

Si quieres reducir aún más tu app, R8 inspeccionará tu código en mayor profundidad a fin de quitar el código que no se usa o, cuando sea posible, volverá a escribirlo de manera que no sea tan detallado. A continuación, se muestran algunos ejemplos de tales optimizaciones:

  • Si tu código nunca toma la rama else {} para una declaración if/else, es posible que R8 quite el código para la rama else {}.
  • Si tu código llama a un método en un solo lugar, es posible que R8 quite el método y lo intercale en un único sitio de llamada.
  • Si R8 determina que una clase tiene una sola subclase única, y esta no se divide en instancias (por ejemplo, una clase de implementación concreta que solo usa una clase básica abstracta), entonces R8 puede combinar ambas clases y quitar una de la app.
  • Para obtener más información, lee la entrada de blog sobre la optimización de R8, de Jake Wharton.

R8 no te permite inhabilitar ni habilitar optimizaciones discretas, ni tampoco modificar el comportamiento de una optimización. De hecho, ignora cualquier regla ProGuard que intente modificar optimizaciones predeterminadas, como -optimizations y - optimizationpasses. Esta restricción es importante porque R8 continúa mejorando y mantener un comportamiento estándar para las optimizaciones ayuda al equipo de Android Studio a solucionar fácilmente cualquier problema que puedas experimentar.

Cómo habilitar optimizaciones más agresivas

R8 incluye un conjunto de optimizaciones adicionales que no están habilitadas de forma predeterminada. Para habilitarlas, incluye lo siguiente en el archivo gradle.properties de tu proyecto:

android.enableR8.fullMode=true
    

Como las optimizaciones adicionales hacen que R8 se comporte de manera diferente a ProGuard, es posible que estas requieran que incluyas reglas ProGuard adicionales a fin de evitar problemas en el tiempo de ejecución. Por ejemplo, supongamos que tu código hace referencia a una clase mediante la API de reflexión de Java. De manera predeterminada, R8 supone que quieres examinar y manipular los objetos de esa clase en el tiempo de ejecución (incluso si tu código no lo hace) y conserva automáticamente la clase y su inicializador estático.

Sin embargo, cuando usas el "modo completo", R8 no realiza esta suposición y, si determina que tu código nunca usa la clase en el tiempo de ejecución, la quitará del archivo DEX final de tu app. Es decir, si quieres conservar la clase y su inicializador estático, deberás incluir una regla de conservación en tu archivo de reglas.

Si tienes algún problema al usar el "modo completo" de R8, consulta la Página de preguntas frecuentes de R8 para obtener una posible solución. Si no puedes resolver el problema, informa un error.

Cómo solucionar problemas con R8

En esta sección, se describen algunas estrategias para solucionar problemas cuando habilitas la reducción, la ofuscación y la optimización con R8. Si no encuentras una solución para tu problema a continuación, también puedes leer la Página de preguntas frecuentes de R8 y la Guía de solución de problemas de ProGuard.

Cómo generar un informe sobre el código quitado (o conservado)

Para ayudarte a resolver determinados problemas de R8, podría resultarte útil ver un informe sobre todos los códigos que R8 quitó de tu app. En cada módulo para el que quieras generar este informe, agrega -printusage <output-dir>/usage.txt en tu archivo de reglas personalizado. Cuando habilites R8 y compiles tu app, R8 mostrará un informe con la ruta de acceso y el nombre de archivo que especificaste. El informe del código que se quitó es similar al siguiente:

androidx.drawerlayout.R$attr
    androidx.vectordrawable.R
    androidx.appcompat.app.AppCompatDelegateImpl
        public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
        public boolean hasWindowFeature(int)
        public void setHandleNativeActionModesEnabled(boolean)
        android.view.ViewGroup getSubDecor()
        public void setLocalNightMode(int)
        final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
        public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
        private static final boolean DEBUG
        private static final java.lang.String KEY_LOCAL_NIGHT_MODE
        static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
    ...
    

En cambio, si quieres ver un informe sobre los puntos de entrada que R8 determina desde las reglas de conservación de tu proyecto, incluye -printseeds <output-dir>/seeds.txt en tu archivo de reglas personalizado. Cuando habilites R8 y compiles tu app, R8 mostrará un informe con la ruta de acceso y el nombre de archivo que hayas especificado. El informe sobre los puntos de entrada conservados es similar al siguiente:

com.example.myapplication.MainActivity
    androidx.appcompat.R$layout: int abc_action_menu_item_layout
    androidx.appcompat.R$attr: int activityChooserViewStyle
    androidx.appcompat.R$styleable: int MenuItem_android_id
    androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
    androidx.lifecycle.FullLifecycleObserverAdapter
    ...
    

Cómo solucionar problemas de reducción de recursos

Cuando reduces recursos, en la ventana Build , se muestra un resumen de los recursos que se quitarán del APK. (Primero debes hacer clic en Toggle view  en el lado izquierdo de la ventana para mostrar la salida de texto detallada de Gradle). Por ejemplo:

:android:shrinkDebugResources
    Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33%
    :android:validateDebugSigning
    

Gradle crea, además, un archivo de diagnóstico llamado resources.txt en <module-name>/build/outputs/mapping/release/ (la misma carpeta de los archivos de salida de ProGuard). Este archivo incluye detalles como qué recursos hacen referencia a otros recursos y cuáles de estos se usan o se quitan.

Por ejemplo, para determinar la razón por la que @drawable/ic_plus_anim_016 aún está en tu APK, abre el archivo resources.txt y busca ese nombre de archivo. Es posible que descubras que se hace referencia a este desde otro recurso, como se muestra a continuación:

16:25:48.005 [QUIET] [system.out] &#64;drawable/add_schedule_fab_icon_anim : reachable=true
    16:25:48.009 [QUIET] [system.out]     &#64;drawable/ic_plus_anim_016
    

Luego, debes saber por qué se puede acceder a @drawable/add_schedule_fab_icon_anim y, si buscas de manera ascendente, encontrarás que el recurso se enumera en "The root reachable resources are:". Por ello, existe una referencia de código a add_schedule_fab_icon_anim (es decir, se encontró el ID de R.drawable en el código alcanzable).

Si no usas una comprobación estricta, los ID de recursos podrían marcarse como accesibles si hay constantes de strings que pareciera que se usan para crear los nombres de los recursos cargados de forma dinámica. En ese caso, si buscas la salida de la compilación del nombre de recurso, es posible que encuentres un mensaje como el siguiente:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
        used because it format-string matches string pool constant ic_plus_anim_%1$d.
    

Si ves una de estas strings y estás seguro de que no se usa para subir el recurso especificado de manera dinámica, puedes usar el atributo tools:discard para notificar al sistema de compilación que lo quite, como se describe en la sección sobre cómo personalizar los recursos que se conservarán.