Visão geral do build do Gradle

Os aplicativos Android geralmente são criados usando o sistema de build Gradle. Antes de detalhar como configurar seu build, vamos conhecer os conceitos por trás dele para que você possa ver o sistema como um todo.

O que é um build?

Um sistema de build transforma seu código-fonte em um aplicativo executável. Os builds geralmente envolvem várias ferramentas para analisar, compilar, vincular e empacotar seu aplicativo ou biblioteca. O Gradle usa uma abordagem baseada em tarefas para organizar e executar esses comandos.

As tarefas encapsulam comandos que traduzem as entradas em saídas. Os plug-ins definem tarefas e a configuração delas. Ao aplicar um plug-in ao build, as tarefas dele são registradas e conectadas usando as entradas e saídas. Por exemplo, ao aplicar o Plug-in do Android para Gradle (AGP) ao arquivo de build, todas as tarefas necessárias para criar um APK ou uma biblioteca do Android são registradas. O plug-in java-library permite criar um jar do código-fonte Java. Existem plug-ins semelhantes para Kotlin e outras linguagens, mas outros plug-ins são destinados a estender plug-ins. Por exemplo, o plug-in protobuf foi criado para adicionar suporte a protobuf em plug-ins atuais, como o AGP ou o java-library.

O Gradle prefere convenção em vez de configuração. Por isso, os plug-ins vêm com bons valores padrão, mas é possível configurar ainda mais o build usando uma linguagem específica do domínio (DSL, na sigla em inglês) declarativa. A DSL foi projetada para que você possa especificar o que criar, em vez de como criar. A lógica nos plug-ins gerencia o "como". Essa configuração é especificada em vários arquivos de build no seu projeto (e subprojetos).

As entradas de tarefas podem ser arquivos e diretórios, além de outras informações codificadas como tipos Java (inteiros, strings ou classes personalizadas). As saídas só podem ser diretórios ou arquivos, já que precisam ser gravadas no disco. Conectar a saída de uma tarefa à entrada de outra vincula as tarefas para que uma precise ser executada antes da outra.

Embora o Gradle permita escrever código arbitrário e declarações de tarefas nos arquivos de build, isso pode dificultar a compreensão do build pelas ferramentas e a manutenção por você. Por exemplo, é possível escrever testes para código dentro de plug-ins, mas não em arquivos de build. Em vez disso, restrinja a lógica de build e as declarações de tarefa a plug-ins (definidos por você ou por outra pessoa) e declare como você quer usar essa lógica nos arquivos de build.

O que acontece quando um build do Gradle é executado?

Os builds do Gradle são executados em três fases. Cada uma dessas fases executa diferentes partes do código que você define nos arquivos de build.

  • A inicialização determina quais projetos e subprojetos são incluídos no build e configura classpaths que contêm seus arquivos de build e plugins aplicados. Esta fase se concentra em um arquivo de configurações em que você declara projetos para criar e os locais de onde buscar plug-ins e bibliotecas.
  • A configuração registra tarefas para cada projeto e executa o arquivo de build para aplicar a especificação de build do usuário. É importante entender que seu código de configuração não terá acesso a dados ou arquivos produzidos durante a execução.
  • A execução realiza a "criação" real do aplicativo. A saída da configuração é um gráfico acíclico dirigido (DAG) de tarefas, que representa todas as etapas de build necessárias solicitadas pelo usuário (as tarefas fornecidas na linha de comando ou como padrões nos arquivos de build). Esse gráfico representa a relação entre as tarefas, seja explícita na declaração de uma tarefa ou com base nas entradas e saídas dela. Se uma tarefa tiver uma entrada que é a saída de outra tarefa, ela precisará ser executada depois da outra. Essa fase executa tarefas desatualizadas na ordem definida no gráfico. Se as entradas de uma tarefa não tiverem mudado desde a última execução, o Gradle vai ignorá-la.

Para mais informações, consulte o ciclo de vida de build do Gradle.

DSLs de configuração

O Gradle usa uma linguagem específica de domínio (DSL) para configurar builds. Essa abordagem declarativa se concentra em especificar seus dados em vez de escrever instruções detalhadas (imperativas). Você pode escrever seus arquivos de build usando Kotlin ou Groovy, mas recomendamos fortemente o uso do Kotlin.

As DSLs tentam facilitar a contribuição de todos, especialistas no assunto e programadores, para um projeto, definindo uma pequena linguagem que representa os dados de uma maneira mais natural. Os plug-ins do Gradle podem estender a DSL para configurar os dados necessários para as tarefas.

Por exemplo, a configuração da parte do Android da sua build pode ser assim:

Kotlin

android {
    namespace = "com.example.app"
    compileSdk {
        version = release(36) {
            minorApiLevel = 1
        }
    }
    // ...

    defaultConfig {
        applicationId = "com.example.app"
        minSdk {
            version = release(23)
        }
        targetSdk {
            version = release(36)
        }
        // ...
    }
}

Groovy

android {
    namespace = 'com.example.app'
    compileSdk {
        version = release(36) {
            minorApiLevel = 1
        }
    }
    // ...

    defaultConfig {
        applicationId = 'com.example.app'
        minSdk {
            version = release(23)
        }
        targetSdk {
            version = release(36)
        }
        // ...
    }
}

Em segundo plano, o código da DSL é semelhante a:

fun Project.android(configure: ApplicationExtension.() -> Unit) {
    ...
}

interface ApplicationExtension {
    var namespace: String?

    fun compileSdk(configure: CompileSdkSpec.() -> Unit) {
        ...
    }

    val defaultConfig: DefaultConfig

    fun defaultConfig(configure: DefaultConfig.() -> Unit) {
        ...
    }
}

Cada bloco na DSL é representado por uma função que usa uma lambda para configurá-lo e uma propriedade com o mesmo nome para acessá-lo. Isso faz com que o código nos arquivos de build pareça mais uma especificação de dados.

Dependências externas

O sistema de build do Maven introduziu uma especificação, um armazenamento e um sistema de gerenciamento de dependências. As bibliotecas são armazenadas em repositórios (servidores ou diretórios), com metadados que incluem a versão e as dependências de outras bibliotecas. Você especifica quais repositórios pesquisar, as versões das dependências que quer usar e o sistema de build faz o download delas durante o build.

Os artefatos Maven são identificados pelo nome do grupo (empresa, desenvolvedor etc.), pelo nome do artefato (o nome da biblioteca) e pela versão desse artefato. Isso geralmente é representado como group:artifact:version.

Essa abordagem melhora significativamente o gerenciamento de builds. Muitas vezes, esses repositórios são chamados de "repositórios Maven", mas tudo se resume à forma como os artefatos são empacotados e publicados. Esses repositórios e metadados foram reutilizados em vários sistemas de build, incluindo o Gradle, que pode publicar nesses repositórios. Os repositórios públicos permitem o compartilhamento para todos, e os repositórios da empresa mantêm as dependências internas internamente.

Você também pode modularizar seu projeto em subprojetos (também conhecidos como "módulos" no Android Studio), que também podem ser usados como dependências. Cada subprojeto produz saídas (como jars) que podem ser consumidas por subprojetos ou pelo projeto de nível superior. Isso pode melhorar o tempo de build ao isolar quais partes precisam ser recriadas, além de separar melhor as responsabilidades no aplicativo.

Vamos entrar em mais detalhes sobre como especificar dependências em Adicionar dependências de build.

Variantes de build

Ao criar um aplicativo Android, geralmente é necessário criar várias variantes. As variantes contêm códigos diferentes ou são criadas com opções diferentes e são compostas de tipos de build e variações de produto.

Os tipos de build variam de acordo com as opções declaradas. Por padrão, o AGP configura os tipos de build "release" e "debug", mas é possível ajustá-los e adicionar mais (talvez para preparo ou testes internos).

Um build de depuração não minimiza nem ofusca o aplicativo, acelerando a criação e preservando todos os símbolos como estão. Ele também marca o aplicativo como "depurável", assinando-o com uma chave de depuração genérica e permitindo o acesso aos arquivos do aplicativo instalados no dispositivo. Isso permite explorar dados salvos em arquivos e bancos de dados enquanto o aplicativo está em execução.

Um build de lançamento otimiza o aplicativo, assina com sua chave de lançamento e protege os arquivos do aplicativo instalado.

Com as variações de produto, é possível mudar a origem incluída e as variantes de dependência do aplicativo. Por exemplo, você pode criar variações "demo" e "full" para seu aplicativo ou talvez "free" e "paid". Você escreve sua origem comum em um diretório de conjunto de origem "main" e substitui ou adiciona origem em um conjunto de origem nomeado de acordo com a variação.

O AGP cria variantes para cada combinação de tipo de build e variação de produto. Se você não definir variações, as variantes vão receber o nome dos tipos de build. Se você definir os dois, a variante será chamada de <flavor><Buildtype>. Por exemplo, com os tipos de build release e debug e as variações demo e full, o AGP vai criar variantes:

  • demoRelease
  • demoDebug
  • fullRelease
  • fullDebug

Próximas etapas

Agora que você já conhece os conceitos de build, confira a estrutura de build do Android no seu projeto.