Otimizar a velocidade do seu build

Tempos de build demorados atrasam o processo de desenvolvimento. Nesta página, vamos apresentar algumas técnicas para ajudar a resolver gargalos de velocidade de build.

O processo geral para melhorar a velocidade de build do app é este:

  1. Otimizar a configuração do build realizando etapas que beneficiam imediatamente a maioria dos projetos do Android Studio.
  2. Criar um perfil para seu build para identificar e diagnosticar alguns dos gargalos mais complicados que podem ser específicos do projeto ou da estação de trabalho.

Sempre que possível, implemente o app que você está desenvolvendo em um dispositivo com o Android 7.0 (nível 24 da API) ou uma versão mais recente. Versões mais recentes da Plataforma Android oferecem recursos melhores para enviar atualizações ao app, como o Android Runtime (ART) e a compatibilidade nativa com vários arquivos DEX.

Observação: depois de lançar o primeiro build limpo, você vai perceber que os builds seguintes (limpos e incrementais) vão apresentar uma performance muito mais rápida, mesmo sem usar as otimizações descritas nesta página. Isso ocorre porque o daemon do Gradle tem um período de “aquecimento” para aumento de desempenho, semelhante a outros processos da JVM.

Otimizar a configuração do build

Siga estas dicas para aumentar a velocidade de build do seu projeto do Android Studio.

Manter suas ferramentas atualizadas

As ferramentas do Android recebem otimizações de build e novos recursos em quase todas as atualizações. Algumas das dicas nesta página presumem que você está usando a versão mais recente. Para aproveitar as otimizações mais recentes, mantenha estes itens atualizados:

Evitar a compilação de recursos desnecessários

Evite compilar e empacotar recursos que não estejam sendo testados (como localizações de idiomas extras e recursos de densidade de tela). Em vez disso, especifique apenas um recurso de idioma e uma densidade de tela para a variação "dev", como mostrado no exemplo abaixo:

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")
        }
        ...
    }
}

Tentar colocar o Portal do plug-in do Gradle por último

No Android, todos os plug-ins são encontrados nos repositórios google() e mavenCentral(). No entanto, seu build pode precisar de plug-ins de terceiros que são resolvidos usando o serviço gradlePluginPortal().

O Gradle pesquisa os repositórios na ordem em que são declarados. Portanto, o desempenho do build é melhorado quando os repositórios listados primeiro contêm a maioria dos plug-ins. Por esse motivo, experimente colocar a entrada gradlePluginPortal() por último no bloco do repositório do arquivo settings.gradle. Na maioria dos casos, isso minimiza o número de pesquisas de plug-ins redundantes e melhora a velocidade do build.

Para ver mais informações sobre como o Gradle navega em vários repositórios, consulte Como declarar vários repositórios na documentação do Gradle.

Usar valores de configuração de build estáticos com seu build de depuração

Sempre use valores estáticos para propriedades que acessam o arquivo do manifesto ou os arquivos de recursos para descobrir o tipo de build de depuração.

O uso de códigos de versão dinâmicos, nomes de versão, recursos ou qualquer outra lógica de build que mude o arquivo de manifesto exige uma criação de app completa sempre que você quiser implementar uma mudança, mesmo que ela precise apenas de um hot swap. Caso sua configuração de build precise dessas propriedades dinâmicas, isole-as nas variantes de build de lançamento e mantenha os valores estáticos nos builds de depuração.

Confira o exemplo abaixo em um arquivo 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.
    // For release builds, it's OK. 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.
    // For release builds, it's OK. 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
        }
    }
}

Usar versões de dependência estáticas

Ao declarar dependências nos arquivos build.gradle, evite usar números de versão dinâmicos (com um sinal de soma no fim, como 'com.android.tools.build:gradle:2.+'). O uso de números de versão dinâmicos pode causar atualizações inesperadas, dificuldade para resolver diferenças entre versões e builds mais lentos, causados pelo Gradle ao verificar se há atualizações. Portanto, use números de versão estáticos.

Criar módulos de biblioteca

Procure no seu app um código que possa ser convertido em um módulo de biblioteca do Android. Modularizar o código dessa maneira permite que o sistema compile somente os módulos que você modificar e armazene em cache essas saídas para builds futuros. A modularização também faz com que a execução de projetos paralelos (link em inglês) seja mais eficaz quando essa otimização é ativada.

Criar tarefas para lógica de build personalizada

Depois de criar um perfil para seu build, se ele mostrar que uma parte relativamente longa do tempo de build é gasta na fase **Configuring Projects**, analise os scripts build.gradle e procure um código para incluir em uma tarefa personalizada do Gradle. Quando alguma lógica de build é movida para uma tarefa, é importante garantir que a tarefa seja executada somente quando necessário, que os resultados possam ser armazenados em cache para builds futuros e que essa lógica de build possa ser executada em paralelo se você ativar a execução de projeto em paralelo. Para saber mais sobre as tarefas para lógica de build personalizada, leia a documentação oficial do Gradle (link em inglês).

Dica: caso seu build inclua um grande número de tarefas personalizadas, organize seus arquivos build.gradle criando classes de tarefas personalizadas (link em inglês). Adicione as classes ao diretório project-root/buildSrc/src/main/groovy/. O Gradle as inclui automaticamente no caminho de classe de todos os arquivos build.gradle do projeto.

Converter imagens em WebP

O WebP é um formato de arquivo de imagem que oferece compactação com perda (como JPEG) e transparência (como PNG), mas com mais qualidade do que JPEG ou PNG.

A redução dos tamanhos de arquivos de imagem, sem que seja necessário compactar o tempo de build, pode agilizar os builds, principalmente se o app usa muitos recursos de imagem. Entretanto, você poderá perceber um pequeno aumento no uso de CPU do dispositivo ao descompactar as imagens WebP. Use o Android Studio para converter as imagens em WebP com facilidade.

Desativar o processamento de PNG

Mesmo que você não converta as imagens PNG em WebP, ainda é possível agilizar o build desativando a compactação automática de imagens sempre que o app for criado.

No plug-in do Android 3.0.0 ou versões mais recentes, por padrão, o processamento de PNG é desativado somente para o tipo de build de "depuração". Para desativar essa otimização para outros tipos de build, adicione o código abaixo ao arquivo 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
        }
    }
}

Como tipos de build ou variações de produto não definem essa propriedade, é necessário definir a propriedade manualmente para true durante a criação da versão de lançamento do app.

Testar o coletor de lixo em paralelo da JVM

O desempenho do build pode ser melhorado configurando o coletor de lixo otimizado da JVM usado pelo Gradle. Enquanto o JDK 8 é configurado para usar o coletor de lixo em paralelo por padrão, o JDK 9 e versões mais recentes são configurados para usar o coletor de lixo G1 (link em inglês).

Para potencialmente melhorar o desempenho do build, recomendamos que você teste seus builds do Gradle com o coletor de lixo em paralelo. Em gradle.properties, defina:

org.gradle.jvmargs=-XX:+UseParallelGC

Se já houver outras opções definidas nesse campo, adicione uma nova opção:

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

Para medir a velocidade do build com diferentes configurações, consulte Criar perfil para seu build.

Aumentar o tamanho de heap da JVM

Se houver builds lentos e, principalmente, se os resultados do Build Analyzer mostrarem que a coleta de lixo levou mais de 15% do tempo de build, aumente o tamanho de heap da Java Virtual Machine (JVM). No arquivo gradle.properties, defina o limite como 4, 6 ou 8 gigabytes, conforme mostrado no exemplo abaixo:

org.gradle.jvmargs=-Xmx6g

Em seguida, faça um teste para conferir se a velocidade de build melhorou. A maneira mais fácil de determinar o tamanho ideal de heap é aumentar um pouco o limite e, em seguida, testar se houve uma melhoria suficiente na velocidade de build.

Caso o coletor de lixo em paralelo da JVM seja utilizado, a linha inteira vai ficar assim:

org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g

Para analisar os erros de memória da JVM, ative a flag HeapDumpOnOutOfMemoryError (link em inglês). Com isso, a JVM gera um heap dump quando o sistema fica sem memória.

Usar classes R não transitivas

Use as classes R não transitivas para builds mais rápidos em apps com vários módulos. Isso ajuda a evitar a duplicação de recursos, garantindo que a classe R de cada módulo contenha apenas referências aos próprios recursos, sem extrair referências das dependências. Assim, os builds ficam mais rápidos e você evita a recompilação.

A partir do Android Studio Bumblebee, as classes R não transitivas são ativadas por padrão para novos projetos. Você pode atualizar projetos criados em versões anteriores do Android Studio para usar classes R não transitivas. Para isso, acesse Refactor > Migrate to Non-Transitive R Classes.

Para saber mais sobre os recursos de app e a classe R, consulte a Visão geral dos recursos de aplicativo.

Desativar a sinalização do Jetifier

Como a maioria dos projetos usa bibliotecas AndroidX diretamente, você pode remover a flag Jetifier para melhorar a performance do build. Para remover a flag Jetifier, defina android.enableJetifier=false no arquivo gradle.properties.

O Build Analyzer pode fazer uma verificação para definir se a flag pode ser removida com segurança para permitir que o projeto tenha um build com melhor performance e possa ser migrado das bibliotecas de Suporte do Android descontinuadas. Para saber mais sobre o Build Analyzer, consulte Resolver problemas de performance do build.

Usar o armazenamento de configuração em cache (experimental)

O armazenamento de configuração em cache é um recurso experimental que permite que o Gradle registre informações sobre o gráfico de tarefas de build e as reutilize nos builds seguintes. Assim, não é necessário configurar o build inteiro novamente.

Para ativar o armazenamento da configuração em cache, siga estas etapas:

  1. Verifique se todos os plug-ins do projeto são compatíveis.

    Use o Build Analyzer para verificar se o projeto é compatível com o armazenamento da configuração em cache. O Build Analyzer executa uma sequência de builds de teste para determinar se o recurso pode ser ativado no projeto. Consulte o problema 13490 (link em inglês) para acessar uma lista de plug-ins com suporte.

  2. Adicione o código abaixo ao arquivo gradle.properties:

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

Quando o armazenamento da configuração em cache estiver ativado, a saída do build vai mostrar a mensagem Calculating task graph as no configuration cache is available for tasks na primeira execução do projeto. Nas execuções seguintes, a saída do build vai mostrar a mensagem Reusing configuration cache.

Para saber mais sobre o armazenamento da configuração em cache, consulte a postagem do blog Análise aprofundada do armazenamento da configuração em cache e a documentação do Gradle sobre armazenamento da configuração em cache (links em inglês).