Cómo optimizar tu velocidad de compilación

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

Los tiempos de compilación prolongados ralentizan el proceso de desarrollo. En esta página, se ofrecen algunas técnicas para ayudarte a resolver cuellos de botella en la velocidad de compilación.

El proceso general de mejora de la velocidad de compilación es el siguiente:

  1. Optimiza la configuración de tu compilación mediante algunos pasos que proporcionan beneficios inmediatos para la mayoría de los proyectos de Android Studio.
  2. Genera un perfil para tu compilación a fin de identificar y diagnosticar algunos de los cuellos de botella más complejos que pueden ser específicos de tu proyecto o estación de trabajo.

Cuando desarrollas tu app, debes implementar un dispositivo con Android 7.0 (nivel de API 24) o una versión posterior siempre que sea posible. En las versiones más recientes de la plataforma de Android, se implementaron mejores mecanismos para introducir actualizaciones en tu app, como Android Runtime (ART) y la compatibilidad nativa con varios archivos DEX.

Nota: Después de tu primera compilación limpia, probablemente observes que las compilaciones posteriores, limpias e incrementales, son mucho más rápidas (aun cuando no se use ninguna de las optimizaciones que se describen en esta página). Esto se debe a que el daemon de Gradle tiene un período de "calentamiento" en el que aumenta el rendimiento, fenómeno similar al de otros procesos de equipos virtuales Java (JVM).

Cómo optimizar la configuración de tu compilación

Sigue estas sugerencias para mejorar la velocidad de compilación de tu proyecto de Android Studio.

Cómo mantener tus herramientas actualizadas

Las herramientas de Android reciben optimizaciones de compilación y funciones nuevas prácticamente con todas las actualizaciones, y, para algunas de las sugerencias que encontrarás en esta página, se presupone que usas la última versión. Para aprovechar las últimas optimizaciones, mantén actualizado lo siguiente:

Cómo evitar compilar recursos innecesarios

Evita compilar y empaquetar recursos que no vayas a probar (como localizaciones de idioma adicionales y recursos de densidad de pantalla). También puedes hacerlo especificando únicamente un recurso de idioma y una densidad de pantalla para la variante "dev", como se muestra en el siguiente ejemplo:

Groovy

android {
    ...
    productFlavors {
        dev {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations "en", "xxhdpi"
        }
        ...
    }
}

Kotlin

android {
    ...
    productFlavors {
        create("dev") {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations("en", "xxhdpi")
        }
        ...
    }
}

Experimenta colocando el Portal de complementos de Gradle en último lugar

En Android, todos los complementos se encuentran en los repositorios google() y mavenCentral(). Sin embargo, es posible que tu compilación necesite complementos de terceros que se resuelvan mediante el servicio gradlePluginPortal(). Gradle busca repositorios en el orden en que se declaran, por lo que el rendimiento de compilación se optimiza si los repositorios enumerados primero contienen la mayoría de los complementos. Por lo tanto, experimenta con la entrada gradlePluginPortal() colocándola en el último bloque del repositorio de tu archivo settings.gradle. En la mayoría de los casos, esto minimiza la cantidad de búsquedas redundantes de complementos y mejora la velocidad de compilación.

Para obtener más información sobre cómo Gradle navega por varios repositorios, consulta Declaring multiple repositories (en inglés) en la documentación de Gradle.

Cómo usar valores de configuración de compilación estáticos con tu compilación de depuración

Usa siempre valores estáticos o no modificables para las propiedades que se incluyen en el archivo de manifiesto o los archivos de recursos para tu tipo de compilación de depuración.

Por ejemplo, para usar códigos de versión dinámicos, nombres de versión, recursos o cualquier otra lógica de compilación que modifique el archivo de manifiesto, se requiere una compilación de la app completa cada vez que deseas ejecutar un cambio, aunque en otras circunstancias la modificación solo hubiera requerido un intercambio directo. Si tu configuración de compilación requiere esas propiedades dinámicas, debes aislarlas de las variantes de tu compilación de lanzamiento y conservar valores estáticos para tus compilaciones de depuración. Para ver un ejemplo, consulta a continuación el archivo build.gradle.

Groovy

int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        // Making either of these two values dynamic in the defaultConfig will
        // require a full app build and reinstallation because the AndroidManifest.xml
        // must be updated.
        versionCode 1
        versionName "1.0"
        ...
    }

    // The defaultConfig values above are fixed, so your incremental builds don't
    // need to rebuild the manifest (and therefore the whole app, slowing build times).
    // But for release builds, it's okay. So the following script iterates through
    // all the known variants, finds those that are "release" build types, and
    // changes those properties to something dynamic.
    applicationVariants.all { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch;
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName;
        }
    }
}

Kotlin

val MILLIS_IN_MINUTE = 1000 * 60
val minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        // Making either of these two values dynamic in the defaultConfig will
        // require a full app build and reinstallation because the AndroidManifest.xml
        // must be updated.
        versionCode = 1
        versionName = "1.0"
        ...
    }

    // The defaultConfig values above are fixed, so your incremental builds don't
    // need to rebuild the manifest (and therefore the whole app, slowing build times).
    // But for release builds, it's okay. So the following script iterates through
    // all the known variants, finds those that are "release" build types, and
    // changes those properties to something dynamic.
    applicationVariants.forEach { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName
        }
    }
}

Cómo usar versiones de dependencias estáticas

Cuando declares dependencias en tus archivos build.gradle, deberás evitar usar números de versión con un signo más al final, como 'com.android.tools.build:gradle:2.+'. El uso de números de versión dinámicos puede generar actualizaciones de versión inesperadas, dificultar la resolución de diferencias de versión y ralentizar las compilaciones debido a que Gradle busca actualizaciones. Como alternativa, debes usar números de versión estáticos o hard-coded.

Cómo crear módulos de biblioteca

Busca en tu app código que puedas convertir en un módulo de biblioteca de Android. La modularización de tu código de esta manera permite que el sistema de compilación compile solo los módulos que modifiques y almacene en caché los resultados para compilaciones futuras. También aporta más eficacia a la ejecución de proyectos paralelos (cuando habilitas esa optimización).

Cómo crear tareas para lógica de compilación personalizada

Una vez que crees un perfil para la compilación, si en este se muestra que una fracción relativamente larga del tiempo de compilación se dedica a la etapa de "configuración de proyectos", revisa tus secuencias de comandos de build.gradle y busca código que puedas incluir en una tarea de Gradle personalizada. Si se traslada parte de la lógica de compilación a una tarea, que se ejecutará únicamente cuando sea necesario, los resultados se pueden almacenar en caché para compilaciones futuras y la lógica de compilación reunirá las condiciones con el objetivo de ejecutarse en paralelo (si habilitas la ejecución de objetos en paralelo). Para obtener más información, lee la documentación oficial de Gradle.

Sugerencia: Si tu compilación incluye una gran cantidad de tareas personalizadas, te recomendamos ordenar tus archivos build.gradle creando clases de tareas personalizadas. Agrega tus clases al directorio project-root/buildSrc/src/main/groovy/; Gradle los incluirá automáticamente en la ruta de clase para todos los archivos build.gradle de tu proyecto.

Convierte imágenes a WebP

WebP es un formato de archivo de imagen que proporciona compresión con pérdida (como JPEG) y transparencia (como PNG), pero puede ofrecer mejor compresión que estos otros dos formatos. Reducir los tamaños de los archivos de imagen sin necesidad de realizar compresión de tiempo de compilación puede acelerar tus compilaciones, en particular, si tu app usa una gran cantidad de recursos de imagen. No obstante, puedes observar un pequeño incremento en el uso de la CPU del dispositivo mientras se descomprimen las imágenes WebP. Con Android Studio, puedes convertir tus imágenes a WebP con facilidad.

Cómo inhabilitar la compresión de PNG

Si no puedes (o no deseas) convertir tus imágenes PNG a WebP, puedes acelerar tu compilación mediante la inhabilitación de la compresión automática de imágenes cada vez que compilas tu app. Si usas un complemento de Android 3.0.0 o posterior, la compresión de PNG está inhabilitada de forma predeterminada solo para el tipo de compilación "debug". Si quieres inhabilitar esta optimización para otros tipos de compilación, agrega lo siguiente a tu archivo build.gradle:

Groovy

android {
    buildTypes {
        release {
            // Disables PNG crunching for the release build type.
            crunchPngs false
        }
    }
}

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Disables PNG crunching for the release build type.
            isCrunchPngs = false
        }
    }
}

Debido a que los tipos de compilación o las variantes de productos no definen esta propiedad, deberás fijarla manualmente en true cuando compiles la versión de actualización de tu app.

Cómo configurar el recolector de elementos no utilizados de JVM

El rendimiento de la compilación puede mejorarse configurando la recolección de elementos no utilizados de JVM óptima que usa Gradle. Mientras que JDK 8 está configurado para usar el recolector de elementos no utilizados paralelo de forma predeterminada, JDK 9 y las versiones posteriores están configurados para usar el recolector de elementos no utilizados G1.

Para mejorar potencialmente el rendimiento de compilación, te recomendamos que pruebes las compilaciones de Gradle con el recolector de elementos no utilizados paralelo. En gradle.properties, configura lo siguiente:

org.gradle.jvmargs=-XX:+UseParallelGC

Si ya hay otras opciones configuradas en este campo, agrega una opción nueva:

org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC

Para medir la velocidad de compilación con diferentes configuraciones, consulta Cómo generar un perfil para tu compilación.

Usa clases R no transitivas

Debes usar clases R no transitivas a fin de obtener compilaciones más rápidas para aplicaciones con varios módulos. Esto ayuda a evitar la duplicación de recursos, ya que garantiza que la clase R de cada módulo solo contenga referencias a sus propios recursos, sin extraer referencias de sus dependencias. De esta manera, se generan compilaciones más rápidas y los beneficios correspondientes de evitar la compilación.

A partir de Android Studio Bumblebee, las clases R no transitivas se activan de forma predeterminada para proyectos nuevos. En el caso de los proyectos creados con versiones anteriores de Studio, puedes actualizarlos para usar clases R no transitivas en Refactor > Migrate to Non-Transitive R Classes.

Para obtener más información sobre los recursos de la app y la clase R, consulta la Descripción general de los recursos de la app.

Cómo inhabilitar la marca Jetifier

La mayoría de los proyectos usan bibliotecas de AndroidX de forma directa, por lo que probablemente puedas quitar la marca Jetifier para obtener un mejor rendimiento de compilación. Para quitar la verificación de Jetifier, configura android.enableJetifier=false en tu archivo gradle.properties. Build Analyzer puede realizar una verificación para ver si la marca se puede quitar de manera segura a fin de permitir que tu proyecto tenga un mejor rendimiento de compilación y migre fuera de las bibliotecas de compatibilidad de Android desactualizadas. Para obtener más información sobre Build Analyzer, consulta Cómo solucionar problemas de rendimiento de compilación.

Cómo usar el almacenamiento en caché de la configuración (experimental)

El almacenamiento en caché de la configuración es una función experimental que permite a Gradle registrar información sobre el gráfico de tareas de compilación y volver a usarla en compilaciones posteriores, por lo que no tiene que volver a configurar toda la compilación. Para habilitar el almacenamiento en caché de la configuración, sigue estos pasos:

  1. Verifica que todos los complementos del proyecto sean compatibles. Usa Build Analyzer para verificar si tu proyecto es compatible con el almacenamiento en caché de la configuración. Ejecuta una secuencia de compilaciones de prueba para determinar si la función se puede activar para el proyecto. También puedes consultar el error n.° 13490 para obtener una lista de los complementos compatibles.
  2. Agrega el siguiente código al archivo gradle.properties:

      org.gradle.unsafe.configuration-cache=true
      # Use this flag sparingly, in case some of the plugins are not fully compatible
      org.gradle.unsafe.configuration-cache-problems=warn

  3. Cuando el almacenamiento en caché de la configuración está habilitado, la primera vez que ejecutas tu proyecto, el resultado de la compilación debe decir Calculating task graph as no configuration cache is available for tasks. Durante las ejecuciones posteriores, el resultado de la compilación debería decir Reusing configuration cache.
Para obtener más información sobre el almacenamiento en caché de la configuración, consulta la entrada de blog Configuration caching deep dive (disponible en inglés) y la documentación sobre el almacenamiento en caché de la configuración.