Os aplicativos Android geralmente são criados usando o sistema de build do Gradle. Antes de entrarmos em detalhes sobre como configurar o build, vamos conhecer os conceitos por trás dele para que você possa entender o sistema como um todo.
O que é um build?
Um sistema de build transforma o 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 a configuração delas. Aplicar
um plug-in ao build registra as tarefas e as conecta usando as
entradas e saídas. Por exemplo, aplicar o Plug-in do Android para Gradle (AGP, na sigla em inglês)
ao arquivo de build registra todas as tarefas necessárias para criar um APK ou uma
biblioteca Android. O plug-in java-library
permite criar um JAR a partir 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
tem como objetivo adicionar
suporte a protobuf a plug-ins existentes, como AGP ou java-library
.
O Gradle prefere convenções em vez de configurações, então os plug-ins vêm com bons valores padrão prontos para uso, mas você pode configurar ainda mais o build usando uma linguagem específica de domínio (DSL) declarativa. O DSL foi projetado para que você possa especificar o que criar, em vez de como criá-lo. A lógica nos plug-ins gerencia o "como". Essa configuração é especificada em vários arquivos de build no projeto (e subprojetos).
As entradas de tarefas podem ser arquivos e diretórios, bem como outras informações codificadas como tipos Java (inteiros, strings ou classes personalizadas). As saídas só podem ser diretórios ou arquivos, porque 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 ofereça suporte à gravação de 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 dele. Por exemplo, é possível escrever testes para código em plug-ins, mas não em arquivos de build. Em vez disso, restrinja as declarações de lógica de build e de tarefas a plug-ins (definidos por você ou outra pessoa) e declare como 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 iniciação determina quais projetos e subprojetos são incluídos no build e configura classpaths que contêm os arquivos de build e os plug-ins aplicados. Esta 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 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 que foram solicitadas pelo usuário (as tarefas fornecidas na linha de comando ou como padrão nos arquivos de build). Esse gráfico representa a relação entre as tarefas, explícita na declaração de uma tarefa ou com base nas entradas e saídas dela. Se uma tarefa tiver uma entrada que seja 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) para configurar builds. Essa abordagem declarativa se concentra em especificar seus dados em vez de escrever instruções (imperativas) detalhadas. 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, em um projeto, definindo uma pequena linguagem que representa os dados de uma maneira mais natural. Os plug-ins do Gradle podem estender o DSL para configurar os dados necessários para as tarefas.
Por exemplo, a configuração da parte do Android do seu build pode ser assim:
Kotlin
android { namespace = "com.example.app" compileSdk = 34 // ... defaultConfig { applicationId = "com.example.app" minSdk = 34 // ... } }
Groovy
android { namespace 'com.example.myapplication' compileSdk 34 // ... defaultConfig { applicationId "com.example.myapplication" minSdk 24 // ... } }
Em segundo plano, o código DSL é semelhante a:
fun Project.android(configure: ApplicationExtension.() -> Unit) {
...
}
interface ApplicationExtension {
var compileSdk: Int
var namespace: String?
val defaultConfig: DefaultConfig
fun defaultConfig(configure: DefaultConfig.() -> Unit) {
...
}
}
Cada bloco no 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 de dependência, um sistema de armazenamento e gerenciamento. 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 você 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. Você vai ouvir com frequência que esses repositórios são chamados de "repositórios do Maven", mas tudo se refere à 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 todos, e os repositórios da empresa mantêm as dependências internas.
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 frascos) que podem ser consumidas por subprojetos ou pelo projeto de nível superior. Isso pode melhorar o tempo de build isolando quais partes precisam ser reconstruídas, 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, normalmente é necessário criar várias variantes. As variantes contêm um código diferente ou são criadas com opções diferentes e compostas de tipos de build e variações de produto.
Os tipos de build variam de acordo com as opções de build declaradas. Por padrão, o AGP configura os tipos de build "release" e "debug", mas você pode ajustá-los e adicionar mais (talvez para estágio ou testes internos).
Um build de depuração não minimiza nem ofusca seu aplicativo, acelerando o build e preservando todos os símbolos como estão. Ele também marca o aplicativo como "depuração", assinando-o com uma chave de depuração genérica e permitindo o acesso aos arquivos de aplicativos instalados no dispositivo. Isso permite explorar dados salvos em arquivos e bancos de dados durante a execução do aplicativo.
Um build de lançamento otimiza o aplicativo, o assina com a chave de lançamento e protege os arquivos do aplicativo instalados.
Usando sabores de produto, é possível mudar a origem incluída e as variantes de dependência do aplicativo. Por exemplo, você pode criar variações "demonstração" e "completa" para o aplicativo ou talvez "sem custo financeiro" e "paga". Você escreve a origem comum em um diretório conjunto de origem "principal" e substitui ou adiciona a 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, elas vão receber o nome dos tipos de build. Se você
definir os dois, a variante será nomeada como <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.