Interdependências de ferramentas e bibliotecas

As dependências de build são componentes externos necessários para criar seu projeto. Um build pode depender de bibliotecas, plug-ins, subprojetos, do SDK do Android, de ferramentas como os compiladores Kotlin e Java, de ambientes de desenvolvimento como o Android Studio e do próprio Gradle.

Cada dependência pode exigir outras dependências. Elas são chamadas de dependências transitivas e podem aumentar rapidamente o número total de dependências usadas pelo aplicativo. Quando você quer fazer upgrade de uma dependência, seja uma biblioteca, uma ferramenta ou o SDK do Android, esse upgrade pode ser feito em cascata, atualizando muitas outras dependências.

Muitas vezes, isso não causa problemas, já que muitas bibliotecas seguem um esquema conhecido como controle de versões semântico. Essas bibliotecas restringem os tipos de mudanças que fazem para oferecer compatibilidade com as versões anteriores.

O controle de versões semântico segue um formato major.minor.patch. Por exemplo, no número de versão 4.8.3, 4 é a versão major, 8 é a versão minor e 3 é o número patch. Quando a parte major muda, a biblioteca pode ter alterações interruptivas na API ou no comportamento. Isso pode afetar o comportamento do build ou do aplicativo.

Quando as partes minor (novos recursos) ou patch (correções de bugs) mudam, os desenvolvedores da biblioteca estão informando que ela ainda é compatível e não deve afetar seu aplicativo.

Relacionamentos no build

As versões do Android contêm relações entre:

  • Código-fonte: o código e os recursos que você controla.
  • Dependências de biblioteca: bibliotecas ou módulos externos que seu projeto e subprojetos incluem ao criar
  • Ferramentas: compiladores, plug-ins e SDKs que traduzem sua fonte em um aplicativo ou biblioteca.
Dependências de build e as relações entre elas
Figura 1. Crie relações que transformam

Código-fonte

O código-fonte é o código Kotlin ou Java que você escreve no aplicativo ou na biblioteca. Para saber mais sobre o uso de C++, consulte Android NDK.

O código-fonte depende de bibliotecas (incluindo as de ambiente de execução do Kotlin e do Java) e do SDK do Android, além de exigir o compilador correspondente do Kotlin ou do Java.

Alguns códigos-fonte incluem anotações que exigem processamento adicional. Por exemplo, se você estiver escrevendo código do Jetpack Compose, adicione anotações como @Composable, que precisam ser processadas pelo plug-in do compilador do Kotlin para o Compose. Outras anotações podem ser processadas por um Kotlin Symbol Processor (KSP) ou por ferramentas de processamento de anotações separadas.

Dependências da biblioteca

As bibliotecas contêm bytecode extraído como parte do aplicativo. Pode ser um JAR Java, uma biblioteca do Android (AAR) ou um subprojeto no build. Muitas bibliotecas seguem o controle de versões semântico, que pode ajudar você a entender se elas permanecem compatíveis (ou não) quando são atualizadas.

As bibliotecas geralmente têm dependências transitivas em outras bibliotecas. Isso reduz o que você precisa gerenciar explicitamente. Você especifica as dependências diretas, e o Gradle as extrai junto com as transitivas. O upgrade de dependências diretas também pode fazer upgrade das dependências transitivas.

Às vezes, uma biblioteca pode exigir versões mínimas do SDK do Android no ambiente de execução (minSdk) ou no tempo de compilação (compileSdk). Isso é necessário quando uma biblioteca usa funções incluídas no SDK do Android ou nas APIs do JDK fornecidas. O minSdk efetivo do aplicativo é o minSdk mais alto solicitado pelo aplicativo e todas as dependências de biblioteca diretas e transitivas.

O uso de algumas bibliotecas pode exigir um plug-in específico do Gradle. Esses plug-ins auxiliares geralmente instalam processadores de símbolos do Kotlin ou outros processadores de anotações que geram código ou modificam a compilação da origem para oferecer suporte ao uso de recursos da biblioteca. Por exemplo, o Jetpack Room inclui anotações e um KSP que as transforma em código gerado para recuperar e modificar dados em um banco de dados. O Jetpack Compose exige que o plug-in do compilador do Compose modifique as funções anotadas para gerenciar como e quando elas são executadas novamente.

Ferramentas

Gradle

O Gradle é a ferramenta de build que lê os arquivos de build e gera o aplicativo ou a biblioteca, além de expor uma API para que os plug-ins ampliem os recursos. O Gradle executa vários processos em uma ou mais máquinas virtuais Java, e os plug-ins Java dele chamam as ferramentas Java dentro do JDK.

Plug-ins do Gradle

Os plug-ins do Gradle estendem o Gradle definindo novas tarefas e configurações. Aplicar um plug-in ao build ativa recursos específicos, configurados como dados nos scripts de build. Para builds do Android, o plug-in mais importante é o Plug-in do Android para Gradle (AGP).

Compiladores

O compilador Kotlin ou Java transforma seu código-fonte em bytecode executável. O compilador do Kotlin expõe uma API de plug-in que permite que a análise externa e a geração de código sejam executadas diretamente no compilador, acessando a estrutura de código analisada.

Plug-ins do compilador

Os plug-ins do compilador realizam análises e geração de código dentro do compilador Kotlin enquanto ele analisa seu código. Eles são instalados quando você aplica os plug-ins do Gradle ao build.

SDK do Android

O SDK do Android contém a plataforma Android e as APIs Java para uma versão específica do Android, além das ferramentas correspondentes. Essas ferramentas ajudam você a gerenciar o SDK, criar aplicativos, se comunicar com dispositivos Android e emulá-los.

Cada versão do SDK do Android oferece APIs Java específicas que podem ser acessadas pelo código-fonte, além de suporte à simplificação para usar essas APIs em versões anteriores do Android.

JDK

O Kit de desenvolvimento em Java, que contém bibliotecas e executáveis Java para compilar fontes e executar aplicativos Java. Há vários JDKs em jogo em um build do Android. Consulte Versões Java em builds do Android para mais detalhes.

Escopos do Gradle

O Gradle agrupa as dependências de biblioteca em escopos diferentes (chamados de configurações na API Gradle), permitindo que você especifique conjuntos diferentes de dependências de biblioteca para serem usados em partes diferentes do build. Por exemplo, talvez você não queira incluir bibliotecas de teste, como JUnit, no aplicativo ou na biblioteca publicada, mas queira que elas estejam presentes ao criar e executar os testes de unidade. Você também usa escopos para adicionar processadores de símbolos ou anotações para analisar seu código.

Por exemplo, o AGP define os escopos implementation e api, que são sua maneira de especificar se uma dependência precisa ser exposta aos usuários do subprojeto. Consulte Configurar dependências para ver as descrições desses e de outros escopos usados em um build do Android.

Adicione dependências de biblioteca no bloco dependencies dos arquivos de build, como strings group:artifact:version:

Kotlin

// In a module-level build script
// explicit dependency strings ("group:artifact:version")
dependencies {
    implementation("com.example:library1:1.2.3")
    api("com.example:library2:1.1.1")
}

Groovy

// In a module-level build script
// explicit dependency strings ("group:artifact:version")
dependencies {
    implementation 'com.example:library1:1.2.3'
    api 'com.example:library2:1.1.1'
}

ou em um catálogo de versões:

# Version catalog - gradle/libs.versions.toml
[versions]
exampleLib = "1.2.3"
examplePlugin = "2.3.4"

[libraries]
example-library = { group = "com.example", name = "library", version.ref = "exampleLib" }

[plugins]
example-plugin = { id = "com.example.plugin", version.ref = "examplePlugin" }

e especifique as variáveis geradas nos arquivos de build:

Kotlin

// In a module-level build script
// Using a version catalog
plugins {
    alias(libs.plugins.example.plugin)
}

dependencies {
    implementation(libs.example.library)
}

Groovy

// In a module-level build script
// Using a version catalog
plugins {
    alias(libs.plugins.example.plugin)
}

dependencies {
    implementation libs.example.library
}