Save the date! Android Dev Summit is coming to Mountain View, CA on November 7-8, 2018.

Otimizar a velocidade da sua compilação

Tempos de compilação longos retardam seu processo de desenvolvimento, portanto, esta página oferece algumas técnicas para ajudá-lo a resolver gargalos de velocidade de compilação.

O processo geral para melhorar sua velocidade de compilação é o seguinte:

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

Ao desenvolver seu aplicativo, você deve implantá-lo em um dispositivo com o Android 7.0 (nível de API 24) ou uma versão posterior sempre que possível. Versões mais recentes da plataforma Android implementam mecânicas melhores para enviar atualizações para seu aplicativo, como o Android Runtime (ART) e o suporte nativo para vários arquivos DEX.

Observação: depois da sua primeira compilação limpa, você poderá perceber que compilações subsequentes (limpas e incrementais), têm um desempenho muito mais rápido, mesmo sem o uso de qualquer uma das otimizações descritas nesta página. Isso ocorre porque o daemon do Gradle tem um período de “aquecimento” de aumento de desempenho, semelhante a outros processos de JVM.

Otimizar a configuração da sua compilação

Siga estas dicas para aumentar a velocidade de compilação do seu projeto do Android Studio.

Manter suas ferramentas atualizadas

As ferramentas do Android recebem otimizações de compilação e novos recursos com quase todas as atualizações e algumas das dicas nesta página presumem que você está usando a versão mais recente. Para aproveitar as últimas otimizações, mantenha os seguintes itens atualizados:

Criar uma variante de compilação para desenvolvimento

Muitas das configurações de que você precisa ao preparar seu aplicativo para lançamento não são necessários durante o desenvolvimento do aplicativo. A ativação de processos de compilação desnecessários reduz a velocidade das suas compilações incrementais e limpas, portanto, configure uma variante de compilação que mantém apenas as configurações de compilação necessárias para o desenvolvimento do aplicativo. O exemplo a seguir cria uma variação "dev" e uma variação "prod" (para as configurações da sua versão de lançamento):

android {
  ...
  defaultConfig {...}
  buildTypes {...}
  productFlavors {
    // When building a variant that uses this flavor, the following configurations
    // override those in the defaultConfig block.
    dev {
      // To avoid using legacy multidex when building from the command line,
      // set minSdkVersion to 21 or higher. When using Android Studio 2.3 or higher,
      // the build automatically avoids legacy multidex when deploying to a device running
      // API level 21 or higher—regardless of what you set as your minSdkVersion.
      minSdkVersion 21
      versionNameSuffix "-dev"
      applicationIdSuffix '.dev'
    }

    prod {
      // If you've configured the defaultConfig block for the release version of
      // your app, you can leave this block empty and Gradle uses configurations in
      // the defaultConfig block instead. You still need to create this flavor.
      // Otherwise, all variants use the "dev" flavor configurations.
    }
  }
}

Se sua configuração de compilação já usa variações de produto para criar diferentes versões do seu aplicativo, você pode combinar as configurações "dev" e "prod" com essas variações ao usar as dimensões de variação. Por exemplo, se você já configurou uma variação "demo" e "full", pode usar a seguinte configuração de exemplo para criar variações combinadas, como "devDemo" e "prodFull":

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.

  flavorDimensions "stage", "mode"

  productFlavors {
    dev {
      dimension "stage"
      minSdkVersion 21
      versionNameSuffix "-dev"
      applicationIdSuffix '.dev'
      ...
    }

    prod {
      dimension "stage"
      ...
    }

    demo {
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }
  }
}

Evitar a compilação de recursos desnecessários

Evite compilar e empacotar recursos que não estão sendo testados (como localizações de idiomas adicionais e recursos de densidade de tela). Para fazer isso, basta especificar um recurso de idioma e uma densidade de tela para sua variação "dev", conforme é mostrado no exemplo a seguir:

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

Desativar Crashlytics para suas compilações de depuração

Se não precisar gerar um relatório do Crashlytics, agilize suas compilações de depuração desativando esse plug-in da seguinte maneira:

android {
  ...
  buildTypes {
    debug {
      ext.enableCrashlytics = false
    }
}

Você também precisa desativar o kit do Crashlytics no tempo de execução para compilações de depuração ao alterar a forma com a qual você inicializa o suporte para Fabric no aplicativo, como é mostrado abaixo:

// Initializes Fabric for builds that don't use the debug build type.
Crashlytics crashlyticsKit = new Crashlytics.Builder()
    .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
    .build();

Fabric.with(this, crashlyticsKit);

Se quiser usar o Crashlytics com suas compilações de depuração, você ainda pode agilizar as compilações impedindo que o Crashlytics atualize recursos do aplicativo com seu ID de compilação exclusivo em cada compilação. Para impedir que o Crashlytics atualize constantemente seu ID de compilação, adicione o seguinte ao seu arquivo build.gradle:

android {
  ...
  buildTypes {
    debug {
      ext.alwaysUpdateBuildId = false
    }
}

Para saber mais sobre como otimizar suas compilações ao usar o Crashlytics, leia a documentação oficial.

Usar valores de configuração de compilação estáticos com sua compilação de depuração

Sempre use valores estáticos/rígidos para propriedades que acessam o arquivo do manifesto ou os arquivos de recursos para seu tipo de compilação de depuração. Se os valores no seu arquivo de manifesto ou os recursos do seu aplicativo precisarem ser atualizados em cada compilação, o Instant Run não poderá realizar uma troca de código. Ele deverá compilar e instalar um novo APK.

Por exemplo, usar códigos de versão dinâmicos, nomes de versão, recursos ou qualquer outra lógica de compilação que altere o arquivo de manifesto exige uma compilação de APK completa sempre que você quiser executar uma alteração, mesmo que a alteração em si precise apenas de um hot swap. Se sua configuração de compilação exigir essas propriedades dinâmicas, isole-as em suas variantes de compilação de lançamento e mantenha os valores estáticos para as compilações de depuração, conforme é mostrado no arquivo build.gradle abaixo.

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 APK build and reinstallation because the AndroidManifest.xml
        // must be updated (which is not supported by Instant Run).
        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 APK, 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;
        }
    }
}

Usar versões de dependência estáticas

Ao declarar dependências nos seus arquivos build.gradle, você deve evitar o uso de números de versão com um sinal de soma no final, como 'com.android.tools.build:gradle:2.+'. Usar números de versão dinâmicos pode causar atualizações de versão inesperadas, dificuldade para resolver diferenças de versão e compilações mais lentas causadas pelo Gradle verificando se há atualizações. Em vez disso, você deve usar números de versão estáticos/rígidos.

Ativar o modo off-line

Se você estiver usando uma conexão lenta, seus tempos de compilação podem ser prejudicados quando o Gradle tentar usar recursos de rede para resolver dependências. Você pode solicitar que o Gradle evite usar recursos de rede usando apenas os artefatos armazenados localmente em cache.

Para usar o Gradle off-line ao compilar com o Android Studio, faça o seguinte:

  1. Abra a janela Preferences clicando em File > Settings (no Mac, Android Studio > Preferences).
  2. No painel esquerdo, clique em Build, Execution, Deployment > Gradle.
  3. Marque a caixa de seleção Offline work.
  4. Clique em Apply ou OK.

Se você estiver compilando pela linha de comando, passe a opção --offline.

Ativar a configuração sob demanda

Para que o Gradle saiba exatamente como compilar seu aplicativo, o sistema de compilação configura todos os módulos do seu projeto e suas dependências antes de cada compilação (mesmo se você estiver compilando e testando apenas um módulo). Isso retarda o processo de compilação para grandes projetos com vários módulos. Para solicitar que o Gradle configure somente os módulos que você deseja compilar, ative a configuração sob demanda seguindo estas etapas:

  1. Abra a janela Preferences clicando em File > Settings (no Mac, Android Studio > Preferences).
  2. No painel esquerdo, clique em Build, Execution, Deployment > Compiler.
  3. Marque a caixa de seleção Configure on demand.
  4. Clique em Apply ou OK.

Criar módulos de biblioteca

Procure em seu aplicativo código que possa ser convertido em um módulo de biblioteca do Android. Modularizar seu código dessa maneira permite que o sistema de compilação compile somente os módulos que você modificar e armazene em cache essas saídas pra futuras compilações. Isso também torna a configuração sob demanda e a execução de projeto em paralelo mais eficazes (quando você ativa esses recursos).

Criar tarefas para lógica de compilação personalizada

Depois que você criar um perfil de compilação, se ele mostrar que uma parte relativamente grande do tempo de compilação é passado na fase “Configurar projetos”, analise seus scripts build.gradle e busque código que você possa incluir em uma tarefa personalizada do Gradle. Ao mover alguma lógica de compilação para uma tarefa, ela é executada somente quando necessário, os resultados podem ser armazenados em cache para compilações futuras e essa lógica de compilação se qualifica para execução em paralelo (se você ativar a execução de projeto em paralelo). Para saber mais, leia a documentação oficial do Gradle.

Dica: se sua compilação incluir uma grande quantidade de tarefas personalizadas, poderá ser necessário organizar seus arquivos build.gradle ao criar classes de tarefas personalizadas. Adicione suas classes ao diretório project-root/buildSrc/src/main/groovy/ e o Gradle as incluirá automaticamente no caminho de classe de todos os arquivos build.gradle do seu projeto.

Configurar dexOptions e ativar pré-dexação de biblioteca

O Android Plugin fornece o dexOptions para que você possa configurar as seguintes propriedades de compilação DEX, que poderão melhorar as velocidades de compilação:

  • preDexLibraries: declara se as dependências da biblioteca devem ser pré-dexadas para que suas compilações incrementais sejam mais rápidas. Como esse recurso pode tornar suas compilações limpas mais lentas, pode ser pertinente desativá-lo para seu servidor de integração contínua.
  • maxProcessCount: define o número máximo de threads a ser usado ao executar dex-in-process. O padrão é 4.
  • javaMaxHeapSize: define o tamanho de heap máximo para o compilador DEX. Em vez de definir essa propriedade, você deve aumentar o tamanho de heap do Gradle (que é compartilhado com o compilador DEX quando dex-in-process está ativado).

Por exemplo:

android {
  ...
  dexOptions {
    preDexLibraries true
    maxProcessCount 8
    // Instead of setting the heap size for the DEX process, increase Gradle's
    // heap size to enable dex-in-process. To learm more, read the next section.
    // javaMaxHeapSize "2048m"
  }
}

Você deve experimentar com essas configurações incrementando seus valores e observando os efeitos ao criar um perfil para sua compilação. Pode haver um impacto negativo no desempenho se você alocar recursos demais para o processo.

Observação: se o daemon do Gradle já estiver em execução, você deverá interromper o processo antes de inicializá-lo com novas configurações. Você pode encerrar o daemon do Gradle selecionando View > Tool Windows > Terminal e inserindo o seguinte comando: gradlew --stop.

Aumentar o tamanho de heap do Gradle e ativar dex-in-process

Dex-in-process executa o compilador DEX dentro do processo de compilação em vez de em um processo de VM separado. Isso torna as compilações limpas e incrementais mais rápidas. Por padrão, novos projetos criados com o Android Studio 2.1 e versões posteriores alocam memória suficiente para o processo de compilação para ativar esse recurso. Se você não criou seu projeto usando o Android Studio 2.1 ou uma versão posterior, é necessário definir o tamanho de heap máximo do daemon do Gradle para pelo menos 1536 MB. O exemplo a seguir define o tamanho de heap do Gradle para 2048 MB no arquivo gradle.properties do seu projeto:

org.gradle.jvmargs = -Xmx2048m

Alguns projetos maiores podem se beneficiar de um tamanho de heap ainda maior do Gradle Entretanto, se você estiver usando uma máquina com pouca memória, você poderá precisar configurar o ambiente de desenvolvimento integrado para usar menos memória. Para saber como mudar a quantidade de recursos alocados para o ambiente de desenvolvimento integrado e como o Gradle afeta o desempenho da sua compilação, acesse para a seção de criar perfis para sua compilação.

Observação: se o daemon do Gradle já estiver em execução, você deverá interromper o processo antes de inicializá-lo com novas configurações. Você pode encerrar o daemon do Gradle selecionando View > Tool Windows > Terminal e inserindo o seguinte comando: gradlew --stop.

Se você definir um valor para android.dexOptions.javaMaxHeapSize no arquivo build.gradle do seu módulo, que controla o tamanho de heap para o compilador DEX, será preciso definir o tamanho de heap do Gradle de forma que ele tenha 512 MB a mais do que o valor definido para a propriedade javaMaxHeapSize e que tenha pelo menos 1536 MB. Por exemplo, se você definir javaMaxHeapSize para 1280 MB, deverá definir org.gradle.jvmargs para pelo menos 1792 MB (1280 + 512), mas um tamanho de heap ainda maior é melhor. Não é preciso especificar um valor para javaMaxHeapSize para ativar dex-in-process. Se você excluir javaMaxHeapSize da configuração da sua compilação, basta garantir que o tamanho de heap do Gradle seja definido para 1536 MB ou mais.

Converter imagens para WebP

WebP é um formato de arquivo de imagem que proporciona compactação com perda (como JPEG) e transparência (como PNG), mas com mais qualidade do que JPEG ou PNG. Reduzir os tamanhos de arquivos de imagem, sem precisar realizar compactação do tempo de compilação, pode agilizar suas compilações, especialmente se seu aplicativo usar muitos recursos de imagem. Entretanto, você poderá perceber um pequeno aumento no uso de CPU do dispositivo ao descompactar as imagens WebP. Ao usar o Android Studio, é fácil converter suas imagens para WebP.

Desativar a análise de PNG

Se você não puder (ou desejar) converter suas imagens PNG em WebP, ainda será possível agilizar a compilação desativando a compactação automática de imagens sempre que compilar seu aplicativo. Para desativar essa otimização, adicione o seguinte ao seu arquivo build.gradle:

android {
  ...
  aaptOptions {
    cruncherEnabled false
  }
}

Como tipos de compilação ou variações de produto não definem essa propriedade, você deve definir essa propriedade manualmente para true ao compilar a versão de lançamento do aplicativo.

Ativar o Instant Run

O Instant Run reduz significativamente o tempo necessário para atualizar seu aplicativo enviando determinados códigos e alterações de recursos sem compilar um novo APK e, em alguns casos, sem precisar reiniciar a atividade atual. Use o Instant Run clicando em Apply Changes depois de fazer as alterações no aplicativo e ele será ativado por padrão quando você fizer o seguinte:

  • Compile seu aplicativo usando uma variante de compilação de depuração.
  • Use o Android Plugin for Gradle versão 2.3.0 ou posterior.
  • Defina minSdkVersion para 15 ou mais no arquivo build.gradle de nível de módulo do seu aplicativo.
  • Implante seu aplicativo em um dispositivo com Android 5.0 (nível de API 21) e versões posteriores clicando em Run .

Se não vir o botão Apply Changes na barra de ferramentas após atender a esses requisitos, garanta que o Instant Run não está desativado nas configurações do ambiente de desenvolvimento integrado seguindo estas etapas:

  1. Abra a caixa de diálogo Settings ou Preferences.
  2. Navegue até Build, Execution, Deployment > Instant Run.
  3. Certifique-se de que a opção Enable Instant Run esteja marcada.

Ativar o cache de compilações

O cache de compilações armazena certas saídas geradas pelo Android Plugin for Gradle ao compilar seu projeto (como AARs não empacotados e dependências remotas pré-dexadas). Suas compilações limpas são muito mais rápidas quando você usa o cache, pois o sistema de compilação pode simplesmente usar os arquivos no cache durante as compilações subsequentes em vez de recriá-los.

Novos projetos que usam o Android Plugin 2.3.0 e versões posteriores ativam o cache de compilações por padrão (a não ser que você explicitamente desative o cache de compilações). Para saber mais, leia Acelerar compilações limpas com o cache de compilações.

Desativar processadores de anotações

A opção Incremental Java compilation é desativada ao usar processadores de anotações. Se possível, evite o uso de processadores de anotações para se beneficiar de compilar apenas as classes modificadas entre compilações.

Criar perfil para sua compilação

Projetos maiores ou projetos que implementem uma grande quantidade de lógica de compilação personalizada podem exigir que você examine em mais detalhes o processo de compilação para encontrar gargalos. Você pode fazer isso ao criar um perfil de quanto tempo o Gradle leva para executar cada fase do ciclo de compilação e cada tarefa de compilação. Por exemplo, se o perfil da compilação mostrar que o Gradle passa tempo demais configurando seu projeto, ele pode sugerir que é preciso retirar a lógica de compilação personalizada da fase de configuração. Além disso, se a tarefa mergeDevDebugResources consumir uma grande parte do tempo de compilação, isso pode indicar que você deve converter suas imagens em WebP ou desativar a análise de PNG.

Usar perfis para melhorar sua velocidade de compilação tipicamente envolve executar sua compilação com os perfis ativados, fazer alguns ajustes nas configurações da compilação e criar mais perfis para observar os resultados das mudanças.

Para gerar e visualizar um perfil de compilação, execute as seguintes etapas:

  1. Com seu projeto aberto no Android Studio, selecione View > Tool Windows > Terminal para abrir uma linha de comando na raiz do seu projeto.
  2. Execute uma compilação limpa inserindo o seguinte comando. Conforme você criar perfis para sua compilação, você deve executar uma compilação limpa entre cada compilação para a qual você cria um perfil, pois o Gradle pula tarefas quando entradas para uma tarefa (como o código-fonte) não mudam. Dessa forma, uma segunda compilação sem mudanças nas entradas sempre será executada mais rapidamente, pois as tarefas não estão sendo repetidas. Portanto, executar a tarefa clean entre suas compilações garante que o perfil seja feito de todo o processo de compilação.
    // On Mac or Linux, run the Gradle wrapper using "./gradlew".
    gradlew clean
    
  3. Execute uma compilação de depuração de uma das suas variantes de produtos, como a variação “dev”, com os seguintes sinalizadores:
    gradlew --profile --recompile-scripts --offline --rerun-tasks assembleFlavorDebug
    
    • --profile: ativa a criação de perfis.
    • --recompile-scripts: força a recompilação de scripts ignorando o armazenamento em cache.
    • --offline: impede o Gradle de recuperar dependências on-line. Isso garante que qualquer atraso causado pelo Gradle ao tentar atualizar suas dependências não interfira com seus dados de perfil. Você já deve ter compilado seu projeto uma vez para garantir que o Gradle já tenha baixado e armazenado em cache suas dependências.
    • --rerun-tasks: força o Gradle a executar todas as tarefas novamente e ignorar qualquer otimização de tarefa.
  4. Quando a compilação for concluída, use a janela Project para navegar até o diretório project-root/build/reports/profile/ (conforme é mostrado na figura 1).

    Figura 1. Visualização Project indicando a localização dos seus relatórios de perfil.

  5. Clique com o botão direito no arquivo profile-timestamp.html e selecione Open in Browser > Default. O relatório deve ser semelhante ao mostrado na figura 2. Você pode inspecionar cada guia do relatório para saber mais sobre sua compilação. Por exemplo, a guia Task Execution mostra quanto tempo o Gradle levou para executar cada tarefa de compilação.

    Figura 2. Visualizando um relatório em um navegador.

  6. Opcional: antes de fazer alterações nas configurações do projeto ou da compilação, repita o comando na etapa 3, omitindo o sinalizador --rerun-tasks. Como o Gradle tenta poupar tempo evitando repetir a execução de tarefas cujas entradas não foram alteradas (indicadas como UP-TO-DATE na guia Task Execution do relatório, conforme é mostrado na figura 3), você pode identificar quais tarefas estão trabalhando quando não deveriam estar. Por exemplo, se o :app:processDevUniversalDebugManifest não estiver marcado como UP-TO-DATE, ele pode sugerir que a configuração da sua compilação atualize o manifesto dinamicamente com cada compilação. Entretanto, algumas tarefas precisam ser executadas durante cada compilação, como :app:checkDevDebugManifest.

    Figura 3. Visualizando os resultados da execução da tarefa.

Agora que você tem um relatório de perfil de compilação, pode começar a buscar oportunidades de otimização inspecionando as informações em cada guia do relatório. Algumas configurações de compilação exigem experimentação, pois os benefícios podem variar entre projetos e estações de trabalho. Por exemplo, projetos com uma grande base de código podem se beneficiar do uso do ProGuard para remover códigos não utilizados e reduzir o tamanho do APK. Entretanto, projetos menores podem se beneficiar mais com a desativação do ProGuard. Além disso, aumentar o tamanho de heap do Gradle pode afetar negativamente o desempenho em máquinas com pouca memória.

Após fazer uma alteração na configuração da sua compilação, observe os resultados das suas alterações repetindo as etapas acima e gerando um novo perfil de compilação. Por exemplo, a figura 4 mostra um relatório para o mesmo exemplo de aplicativo após aplicar algumas das otimizações básicas descritas nesta página.

Figura 4. Visualizando um novo relatório após otimizar a velocidade da compilação.

Dica: para obter uma ferramenta de criação de perfis mais robusta, considere usar o gerador de perfis de código aberto do Gradle.