Skip to content

Most visited

Recently visited

navigation

Reducir tu código y tus recursos

Para reducir tu archivo APK tanto como sea posible, debes habilitar la reducción para quitar los recursos y el código sin usar de tu compilación de lanzamiento. En esta página se describe la manera de hacerlo y de especificar el código y los recursos que se deben conservar o eliminar durante la compilación.

La reducción de código está disponible con ProGuard, que detecta y quita clases, campos, métodos y atributos sin usar de tu aplicación empaquetada, incluidos aquellos de las bibliotecas de código incluidas (lo que lo convierte en una herramienta valiosa para solucionar el límite de referencia de 64K). ProGuard también optimiza el código de bytes, quita instrucciones de código sin usar y oculta las clases, los archivos y los métodos con nombres cortos restantes. El código oculto hace que se resulte difícil aplicar ingeniería inversa en tu APK, lo cual tiene particular valor cuando tu app usa funciones confidenciales, como la verificación de licencias.

La reducción de recursos está disponible con el complemento de Android para Gradle, que quita los recursos no usados de tu app empaquetada, incluidos los recursos no usados en las bibliotecas de códigos. Funciona junto con la reducción de código, de modo que al quitarse el código sin usar los recursos a los que ya no se haga referencia también puedan quitarse con seguridad.

Las funciones que se indican en este documento dependen de lo siguiente:

Reducir tu código

Para habilitar la reducción de código con ProGuard, agrega minifyEnabled true al tipo de compilación correspondiente en tu archivo build.gradle.

Recuerda que la reducción de código aumenta el tiempo de compilación, por lo que debes evitar usarla en tu compilación de depuración siempre que sea posible. No obstante, es importante que habilites la reducción de código en el APK final que se usará para realizar pruebas, ya que podría introducir errores si no personalizas de manera suficiente el código que se conservará.

Por ejemplo, el siguiente fragmento de un archivo build.gradle habilita la reducción de código para la compilación de la versión:

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

Nota: Android Studio inhabilita ProGuard cuando se usa Instant Run. Si debes reducir el código en compilaciones incrementales, prueba el reductor experimental de Gradle.

Además de la propiedad minifyEnabled, la propiedad proguardFiles define las reglas de ProGuard:

Si deseas añadir más reglas de ProGuard específicas para cada variante de compilación, agrega otra propiedad proguardFiles en el bloque productFlavor correspondiente. Por ejemplo, el siguiente archivo de Gradle agrega flavor2-rules.pro a la clase 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.txt'),
                   'proguard-rules.pro'
        }
    }
    productFlavors {
        flavor1 {
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

Con cada compilación, ProGuard crea los siguientes archivos:

dump.txt
Describe la estructura interna de todos los archivos de clase del APK.
mapping.txt
Proporciona una traducción entre la clase original y la oculta, el método y los nombres de campos.
seeds.txt
Indica las clases y los miembros que no se ocultaron.
usage.txt
Indica el código que se quitó del APK.

Estos archivos se guardan en <module-name>/build/outputs/mapping/release/.

Determinar de forma personalizada el código que se conservará

En algunas situaciones, el archivo de configuración de ProGuard predeterminado (proguard-android.txt) es suficiente y ProGuard elimina todo (y solo) el código no usado. No obstante, muchas situaciones son difíciles para que ProGuard realice un análisis correcto y podría quitar código que tu app realmente necesita. Entre algunos ejemplos de situaciones en las cuales podría quitar código de forma incorrecta se incluyen los siguientes:

Probar tu app debe revelar cualquier error causado por un código quitado de forma inadecuada, pero también puedes buscar el código quitado revisando el archivo de salida usage.txt guardado en <module-name>/build/outputs/mapping/release/.

Para corregir errores y hacer que ProGuard conserve un código en particular, agrega una línea -keep en el archivo de configuración de ProGuard. Por ejemplo:

-keep public class MyClass

También puedes agregar la anotación @Keep al código que desees conservar. La adición de @Keep en una clase hace que esta se mantenga por completo en el estado en que se encuentra. Agregarla a un método o un campo, mantendrá intactos ese método o campo (y su nombre) y el nombre de la clase. Ten en cuenta que esa anotación está disponible solo cuando usas la Biblioteca de compatibilidad con anotaciones.

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 configuración, lee el Manual de ProGuard. En la sección Solución de problemas indica otros problemas comunes que podrías encontrar cuando se recorta tu código.

Decodificar un seguimiento de pila oculto

Después de que ProGuard reduce tu código, es difícil (o imposible) leer un seguimiento de pila porque los nombres de los métodos están ocultos. Afortunadamente, ProGuard crea un archivo mapping.txt cada vez que se ejecuta, el cual muestra la clase, el método y los nombres de campo originales asignados a los nombres ofuscados. ProGuard guarda el archivo en el directorio <module-name>/build/outputs/mapping/release/ de la app.

Ten en cuenta que el archivo mapping.txt se sobrescribe cada vez que creas una compilación de versión con ProGuard; por ello, debes guardar una copia cuidadosamente cada vez que publiques una nueva versión. Al retener una copia del archivo mapping.txt para cada compilación de versión, podrás depurar un problema en caso de que un usuario envíe un seguimiento de pila ofuscado de una versión antigua de tu app.

Al publicar tu app en Google Play, puedes cargar 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 la Google Play Developer Console. Para obtener más información, consulta el artículo del Centro de ayuda en el que se muestra la forma de hacer visibles seguimientos de pila de fallas.

Para convertir un seguimiento de pila ofuscado en uno que puedas leer por ti mismo, usa la secuencia de comandos retrace (retrace.bat en Windows, retrace.sh en Mac/Linux). Se ubica en el directorio <sdk-root>/tools/proguard/. La secuencia de comandos toma el archivo mapping.txt y tu seguimiento de pila, y produce un nuevo seguimiento de pila legible. La sintaxis para usar la herramienta retrace es la siguiente:

retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]

Por ejemplo:

retrace.bat -verbose mapping.txt obfuscated_trace.txt

Si no especificas el archivo de seguimiento de pila, la herramienta retrace realiza la lectura desde una entrada estándar.

Habilitar la reducción de código con Instant Run

Si la reducción de código es importante para ti durante la compilación incremental de tu app, prueba el reductor de código experimental integrado en el complemento de Android para Gradle. A diferencia de ProGuard, este reductor admite Instant Run.

Puedes configurar el reductor del complemento de Android con los mismos archivos de configuración de ProGuard. Sin embargo, el reductor del complemento de Android no ofusca ni optimiza tu código(solo quita el que no se usa). Por ello, debes usarlo solo para tus compilaciones de depuración y debes habilitar ProGuard para tus compilaciones de lanzamiento, de modo que el código de tu APK se ofusque y se optimice.

Para habilitar el reductor del complemento de Android, simplemente fija useProguard en false en tu tipo de compilación “debug” (y mantén minifyEnabled en true).

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

Nota: Si el reductor del complemento de Android inicialmente quita un método, pero luego haces un cambio en el código para que sea posible alcanzar dicho método, Instant Run lo trata como un cambio de código estructural y hace un intercambio en frío.

Reducir tus recursos

La reducción de recursos funciona únicamente junto con la reducción de código. Después de que el reductor de código quita todo el código sin usar, el reductor de recursos puede identificar los recursos que la app todavía usa. Esto ocurre especialmente cuando agregas bibliotecas de código que incluyen recursos; debes quitar el código de biblioteca sin usar de modo que se deje de hacer referencia a los recursos de biblioteca y el reductor de recursos pueda quitarlos.

Para habilitar la reducción de recursos, fija la propiedad shrinkResources en true en tu 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 aún 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.

Nota: El reductor de recursos actualmente no elimina recursos definidos en una carpeta values/ (como strings, dimensiones, estilos y colores). Esto ocurre porque la herramienta empaquetadora de recursos de Android (AAPT) no permite que el complemento para Gradle especifique versiones predefinidas para los recursos. Para obtener información detallada, consulta el error 70869.

Determinar de forma personalizada 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 cada recurso que desees conservar en el atributo tools:keep y 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. La compilación no empaqueta este archivo en tu APK.

Especificar los recursos que se deben descartar podría parecer absurdo dado que podrías eliminarlos, pero puede ser útil cuando uses variantes de compilación. Por ejemplo, podrías disponer todos tus recursos en el directorio común del proyecto y luego crear un archivo keep.xml diferente para cada variante de compilación cuando sepas que haya un recurso que el código parezca usar (y que, por lo tanto, no sea eliminado por el reductor), pero en realidad también sepas que no se usará a través de la variante de compilación específica. También es posible que las herramientas de compilación identifiquen un recurso incorrectamente según se necesite, lo cual es posible porque el compilador agrega los ID de recursos integrados, y existe la posibilidad de que el analizador de recursos no reconozca la diferencia entre un recurso referenciado genuinamente y un valor entero en el código que tiene el mismo valor.

Habilitar comprobaciones de referencia estrictas

Normalmente, el reductor de recursos puede determinar con precisión si se usa un recurso. Sin embargo, si tu código llama a 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 haces esto, el reductor de recursos adopta un comportamiento defensivo de forma predeterminada y marca todos los recursos con formato de nombre similar como posiblemente en uso y no disponibles para su remoción.

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

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. No obstante, puedes desactivar este manejo de tipo “mejor prevenir que curar” y especificar que el reductor de recursos conserve solo aquellos que se usen con certeza. 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 esos recursos manualmente usando el atributo tools:keep.

Quitar recursos alternativos sin usar

El reductor de recursos de Gradle quita solo los recursos a los cuales el código de tu app no hace referencia; esto significa que no quitará recursos alternativos para diferentes configuraciones de dispositivos. De ser necesario, puedes usar la propiedad resConfigs del complemento de Gradle de Android para quitar archivos de recursos alternativos que tu app no necesite.

Por ejemplo, si usas una biblioteca que incluye recursos de idioma (como AppCompat o Google Play Services), tu APK incluye todas las strings de idioma traducidas para los mensajes en esas bibliotecas independientemente de que el resto de tu app se traduzca a 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. Los recursos para idiomas que no se especifiquen se eliminarán.

El siguiente fragmento de código muestra la manera de limitar tus recursos de idioma solo a inglés y francés:

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

De igual manera, puedes personalizar la densidad de pantalla o los recursos de ABI que desees incluir en tu APK usando divisiones de APK a fin de compilar diferentes APK para diferentes dispositivos.

Fusionar recursos duplicados

De forma predeterminada, Gradle también fusiona recursos con nombres idénticos, como elementos de diseño con el mismo nombre que podrían encontrarse 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 solo ocurre cuando dos o más archivos comparten un nombre, tipo o calificador de recurso idéntico. Gradle selecciona el archivo que considera como es la mejor opción entre los duplicados (en función de un orden de prioridad que se describe más adelante) y solo pasa el recurso a la AAPT para su distribución en el archivo de APK.

Gradle busca recursos duplicados en las siguientes ubicaciones:

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 en tus recursos principales y una versión de compilación, Gradle selecciona el de la versión de compilación.

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

Solucionar problemas de reducción de recursos

Cuando reduces recursos, la consola de Gradle muestra un resumen de los recursos que quitó del paquete de la app. 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 los recursos que hacen referencia a otros recursos y los que se usan o se eliminaron.

Por ejemplo, para determinar la razón por la cual @drawable/ic_plus_anim_016 aún está en tu APK, abre el archivo resources.txt y busca ese nombre de archivo. Podrías detectar que se hace referencia desde otro recurso, de la siguiente manera:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

Debes saber el motivo por el cual @drawable/add_schedule_fab_icon_anim es alcanzable, y si realizas una búsqueda hacia arriba encontrarás que el recurso estará incluido en una lista titulada “The root reachable resources are:”. Por ello, existe una referencia de código a add_schedule_fab_icon_anim (es decir, se encontró su R.drawable ID en el código alcanzable).

Si no usas comprobación estricta, los ID de recursos pueden marcarse como accesibles cuando haya constantes de string que puedan usarse a fin de crear nombres de recursos para recursos cargados de forma dinámica. En ese caso, si buscas el resultado de la compilación para el nombre del recurso, podrías encontrar un mensaje como este:

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 cargar 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 Personalizar los recursos que se conservarán.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience. (Dec 2017 Android Platform & Tools Survey)