Os apps Android geralmente são criados usando o sistema de build do Gradle. Antes de detalharmos 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 convertem as entradas em
saídas. Os plug-ins definem as tarefas e as configurações delas. Aplicar
um plug-in ao build registra as tarefas dele e as conecta usando as
entradas e saídas. Por exemplo, aplicar o Plug-in do Android para Gradle (AGP)
ao arquivo de build vai registrar todas as tarefas necessárias para criar um APK ou uma
biblioteca do Android. O plug-in java-library permite criar um jar com base no código-fonte
Java. Existem plug-ins semelhantes para Kotlin e outras linguagens, mas eles
servem para estender plug-ins. Por exemplo, o plug-in protobuf serve para adicionar
suporte ao protobuf a plug-ins atuais, como o AGP ou o java-library.
O Gradle prefere convenção em vez de configuração, então os plug-ins vêm com bons valores padrão prontos para uso, mas é possível configurar ainda mais o build usando uma linguagem específica de 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 (números 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 seja executada antes da outra.
Embora o Gradle permita escrever códigos arbitrários 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 tarefas 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 partes diferentes 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 caminhos de classe que contêm os arquivos de build e os plug-ins aplicados. Essa fase se concentra em um arquivo de configurações em que você declara os projetos a serem criados 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 o código de configuração não terá acesso aos 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, na sigla em inglês) 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 de forma 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 tarefa. 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 do build do Gradle.
DSLs de configuração
O Gradle usa uma linguagem específica de domínio (DSL, na sigla em inglês) 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 o uso do Kotlin.
As DSLs tentam facilitar a contribuição de todos, especialistas em domínio e programadores, para um projeto, definindo uma linguagem pequena 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 do 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) } // ... } }
Nos bastidores, o código 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 se pareça mais com uma especificação de dados.
Dependências externas
O sistema de build do Maven introduziu um sistema de especificação, armazenamento e 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 do 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 à maneira 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 que todos possam usar, 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, isolando as partes que 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 app Android, geralmente você quer criar várias variantes. As variantes contêm códigos diferentes ou são criadas com opções diferentes e são compostas por tipos de build e variações de produto.
Os tipos de build variam as opções de build declaradas. Por padrão, o AGP configura os tipos de build "release" e "debug", mas é possível ajustá-los e adicionar outros, talvez para preparo ou testes internos.
Um build de depuração não minifica nem ofusca o aplicativo, acelerando o build 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 instalado no dispositivo. Isso possibilita analisar dados salvos em arquivos e bancos de dados enquanto o aplicativo é executado.
Um build de lançamento otimiza o aplicativo, o assina com sua chave de lançamento e protege os arquivos do aplicativo instalado.
Usando variações de produto, é possível mudar as variantes de origem e de dependência incluídas no 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 conjunto de origem "principal" e substitui ou adiciona uma origem em um conjunto de origem com o nome da 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 serão nomeadas de acordo com os tipos de build. Se você
definir ambos, 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
as variantes:
demoReleasedemoDebugfullReleasefullDebug
Próximas etapas
Agora que você já conhece os conceitos de build, confira a estrutura de build do Android no seu projeto.