Os aplicativos Android geralmente são criados usando o sistema de build do Gradle. Antes de nos aprofundarmos em como configurar seu build, vamos explorar os conceitos por trás do build 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 e as conecta usando as
entradas e saídas delas. 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 outros idiomas, 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 usar a convenção em vez da configuração, de modo que os plug-ins tenham bons valores padrão prontos para uso, mas é possível configurar ainda mais o build usando uma linguagem específica de domínio (DSL) declarativa. A DSL foi projetada para que você possa especificar o que criar, em vez de como fazer isso. 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 (números 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 a lógica de build e as declarações de tarefas a plug-ins (definidos por você ou 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 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 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 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 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, ela precisará ser executada após a 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á-las.
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 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 para um projeto para todos, especialistas em domínio e programadores, definindo uma pequena linguagem que representa os dados de 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 Android do seu build pode ser semelhante a esta:
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 // ... } }
Internamente, 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 os metadados, incluindo a versão e as dependências delas em 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 é normalmente
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. O Gradle pode publicar nesses repositórios. Os repositórios públicos permitem o compartilhamento para todos usarem, 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 frascos) que podem ser consumidas por subprojetos ou pelo projeto de nível superior. Isso pode melhorar o tempo de compilação, isolando quais partes precisam ser reconstruídas, bem como melhor separação das 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 "versão" e "depuração", mas é possível ajustá-los e adicionar mais (talvez para testes de preparo ou internos).
Um build de depuração não reduz nem ofusca seu app, acelerando o build e preservando todos os símbolos como estão. Ele também marca o app como "depurável", assinando-o com uma chave de depuração genérica e permitindo o acesso aos arquivos do app instalado 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 instalado.
Ao usar variações de produto, é possível mudar a origem e as variantes de dependência incluídas no app. Por exemplo, você pode criar variações "demonstração" e "completa" para o aplicativo ou talvez "gratuita" e "paga". Você escreve a origem comum em um diretório source set "principal" e substitui ou adiciona a origem em um conjunto de origem que tem 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 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
variantes:
demoRelease
demoDebug
fullRelease
fullDebug
Próximas etapas
Agora que você já conhece os conceitos de build, dê uma olhada na estrutura de build do Android no seu projeto.