Esta página mostra as informações sobre como configurar variantes de build para criar diferentes versões do app em um só projeto e como gerenciar corretamente as dependências e configurações de assinatura.
Cada variante de build representa uma versão diferente do seu app que pode ser criada. Por exemplo: você pode querer criar duas versões do app: uma gratuita com conteúdo limitado e outra com mais conteúdo e paga. Você também pode criar builds do app que sejam direcionados a diferentes dispositivos, com base no nível da API ou em outras variações.
Variantes de build são o resultado do uso de um conjunto específico de regras pelo Gradle para combinar configurações, código e recursos nos seus tipos de build e variações de produtos. Embora as variantes de build não sejam configuradas diretamente, é necessário configurar os tipos de build e as variações de produto que compõem essas variantes.
Por exemplo, uma variação de produto "demo" (demonstração) pode especificar alguns recursos
e requisitos de dispositivo, como código-fonte, recursos e níveis
mínimos da API personalizados. Já um tipo de build "debug" (depuração) aplica diferentes configurações de empacotamento e
de build, como opções de depuração e chaves de assinatura. A
variante de build que combina essas duas é a versão "demoDebug" do seu app e inclui uma
combinação de configurações e recursos presentes na variação de produto "demo",
no tipo de build "debug" e no conjunto de origem main/
.
Configurar tipos de build
Você pode criar e configurar tipos de build no bloco android
do arquivo build.gradle.kts
do módulo. Quando você cria
um novo módulo, o Android Studio cria automaticamente os tipos de build de depuração e
de lançamento. Embora o tipo de build de depuração não apareça no arquivo de configuração
do build, o Android Studio o configura com debuggable
true
. Isso permite depurar o app em dispositivos Android seguros e
configura a assinatura de apps com um keystore de depuração genérico.
É possível adicionar o tipo de build de depuração às suas configurações caso você queira adicionar
ou mudar alguma configuração. O exemplo abaixo especifica um
applicationIdSuffix
para o tipo de build de depuração e configura
um tipo de build "staging" (de preparo), que é inicializado usando as configurações do tipo de build de depuração:
Kotlin
android { defaultConfig { manifestPlaceholders["hostName"] = "www.example.com" ... } buildTypes { getByName("release") { isMinifyEnabled = true proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } getByName("debug") { applicationIdSuffix = ".debug" isDebuggable = true } /** * The `initWith` property lets you copy configurations from other build types, * then configure only the settings you want to change. This one copies the debug build * type, and then changes the manifest placeholder and application ID. */ create("staging") { initWith(getByName("debug")) manifestPlaceholders["hostName"] = "internal.example.com" applicationIdSuffix = ".debugStaging" } } }
Groovy
android { defaultConfig { manifestPlaceholders = [hostName:"www.example.com"] ... } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { applicationIdSuffix ".debug" debuggable true } /** * The `initWith` property lets you copy configurations from other build types, * then configure only the settings you want to change. This one copies the debug build * type, and then changes the manifest placeholder and application ID. */ staging { initWith debug manifestPlaceholders = [hostName:"internal.example.com"] applicationIdSuffix ".debugStaging" } } }
Observação: ao fazer mudanças em um arquivo de configuração do build, o Android Studio exige a sincronização do projeto com a nova configuração. Para sincronizar seu projeto, clique em Sync Now na barra de notificações que aparece a cada mudança ou em Sync Project na barra de ferramentas. Se o Android Studio perceber erros na sua configuração, a janela Messages vai aparecer para mostrar o problema.
Para saber mais sobre todas as propriedades que podem ser configuradas com tipos de build,
leia a
referência do BuildType
.
Configurar variações de produtos
Criar variações de produtos é semelhante à criação de tipos de build. Adicione variações de produtos ao
bloco productFlavors
na configuração do build e inclua as definições que você quiser.
As variações de produtos oferecem suporte às mesmas propriedades que
defaultConfig
, porque defaultConfig
pertence à classe
ProductFlavor
. Isso significa que você pode fornecer a configuração
básica para todas as variações do bloco defaultConfig
, e
cada uma delas pode modificar esses valores padrão, como o applicationId
. Para
saber mais sobre o ID do aplicativo, leia
Definir o ID do aplicativo.
Observação: ainda vai ser necessário especificar um nome de pacote usando o
atributo package
no arquivo de manifesto main/
. Você também precisa usar esse
nome de pacote no código-fonte para se referir à classe R
ou para resolver qualquer
atividade relativa ou registro de serviço. Isso permite usar o
applicationId
para fornecer a cada variação de produto um código exclusivo de
empacotamento e distribuição sem precisar mudar o código-fonte.
Todas as variações precisam pertencer a uma dimensão nomeada, que é um grupo de variações de produtos. É preciso atribuir todas as variações a uma dimensão de variações. Caso contrário, este erro de build ocorre:
Error: All flavors must now belong to a named flavor dimension. The flavor 'flavor_name' is not assigned to a flavor dimension.
Se determinado módulo especificar apenas uma dimensão de variações, o Plug-in do Android para Gradle atribuirá automaticamente todas as variações do módulo a essa dimensão.
O exemplo de código abaixo cria uma dimensão de variações chamada "version" (versão) e adiciona
variações de produto "demo" (demonstração) e "full" (completa). Essas variações fornecem os próprios
applicationIdSuffix
e
versionNameSuffix
:
Kotlin
android { ... defaultConfig {...} buildTypes { getByName("debug"){...} getByName("release"){...} } // Specifies one flavor dimension. flavorDimensions += "version" productFlavors { create("demo") { // Assigns this product flavor to the "version" flavor dimension. // If you are using only one dimension, this property is optional, // and the plugin automatically assigns all the module's flavors to // that dimension. dimension = "version" applicationIdSuffix = ".demo" versionNameSuffix = "-demo" } create("full") { dimension = "version" applicationIdSuffix = ".full" versionNameSuffix = "-full" } } }
Groovy
android { ... defaultConfig {...} buildTypes { debug{...} release{...} } // Specifies one flavor dimension. flavorDimensions "version" productFlavors { demo { // Assigns this product flavor to the "version" flavor dimension. // If you are using only one dimension, this property is optional, // and the plugin automatically assigns all the module's flavors to // that dimension. dimension "version" applicationIdSuffix ".demo" versionNameSuffix "-demo" } full { dimension "version" applicationIdSuffix ".full" versionNameSuffix "-full" } } }
Observação: se você tem um app legado (criado antes de
agosto de 2021) distribuído usando APKs no Google Play, atribua o mesmo valor applicationId
a todas as variantes e dê a cada uma delas um
versionCode
diferente Para distribuir diferentes
variantes do seu app separadamente no Google Play, você precisa atribuir um
applicationId
diferente a cada uma delas.
Depois de criar e configurar suas variações de produtos, clique em Sync
Now na barra de notificações. Quando a sincronização é concluída, o Gradle
cria as variantes de build automaticamente com base nos tipos de build e nas variações
de produto e as nomeia de acordo com
<product-flavor><Build-Type>
. Por exemplo, se você
criou as variações de produto "demo" e "full" e manteve os tipos de build padrão
"debug" (depuração) e
"release" (lançamento), o Gradle vai criar as seguintes variantes de build:
-
demoDebug
-
demoRelease
-
fullDebug
-
fullRelease
Para selecionar qual variante de build criar e executar, acesse Build > Select Build Variant e selecione uma variante de build no menu. Para começar a personalizar cada variante de build com os próprios recursos, você precisa criar e gerenciar conjuntos de origem, conforme descrito nesta página.
Alterar o ID do aplicativo para variantes de build
Ao criar um APK ou AAB para o app, as ferramentas de build marcam o app com o
ID do aplicativo definido no bloco defaultConfig
do arquivo
build.gradle.kts
, conforme mostrado no exemplo abaixo. No entanto, para criar versões diferentes do app
para aparecerem como listagens separadas na Google Play Store, como uma versão "free" (gratuita) e uma "pro" (avançada), crie
variantes de build
separadas, cada uma com um ID de aplicativo
diferente.
Nesse caso, defina cada variante de build como uma
variação de produto separada. Para cada variação
dentro do bloco productFlavors
, é possível redefinir a propriedade
applicationId
ou anexar um segmento ao ID do aplicativo padrão
usando applicationIdSuffix
, como demonstrado abaixo:
Kotlin
android { defaultConfig { applicationId = "com.example.myapp" } productFlavors { create("free") { applicationIdSuffix = ".free" } create("pro") { applicationIdSuffix = ".pro" } } }
Groovy
android { defaultConfig { applicationId "com.example.myapp" } productFlavors { free { applicationIdSuffix ".free" } pro { applicationIdSuffix ".pro" } } }
Dessa forma, o ID do aplicativo para uma variação de produto "free" é "com.example.myapp.free".
Você também pode usar applicationIdSuffix
para anexar um segmento de acordo com o
tipo de build da seguinte forma:
Kotlin
android { ... buildTypes { getByName("debug") { applicationIdSuffix = ".debug" } } }
Groovy
android { ... buildTypes { debug { applicationIdSuffix ".debug" } } }
Como o Gradle aplica a configuração do tipo de build de acordo com a variação do produto, o ID do aplicativo para a variante de build "free debug" (depuração sem custo financeiro) agora é "com.example.myapp.free.debug". Isso é útil quando se quer ter o build de depuração e de lançamento no mesmo dispositivo, porque dois apps nunca podem ter o mesmo ID do aplicativo.
Se você tem um app legado (criado antes de agosto de 2021) distribuído usando APKs no Google Play e quer usar a mesma página "Detalhes do app" para distribuir vários APKs, cada um segmentando uma configuração de dispositivo diferente, por exemplo, o nível da API, é necessário usar o mesmo ID do aplicativo para cada variante de build, mas atribuir a cada APK umversionCode
diferente. Para mais
informações, leia sobre
Suporte a vários APKs. A publicação
com AABs não é afetada, porque usa um único artefato com um único
código de versão e ID do aplicativo por padrão.
Dica: se você precisar fazer referência ao ID do aplicativo no
arquivo de manifesto, use o marcador ${applicationId}
em qualquer
atributo do manifesto. Durante a criação, o Gradle substitui essa tag pelo
ID do aplicativo. Para saber mais, acesse Injetar variáveis de build no
manifesto.
Combinar diversas variações de produtos com dimensões de variação
Em alguns casos, é recomendável combinar as configurações de diferentes variações de produto. Por exemplo, recomendamos criar configurações diferentes para as variações de produto "full" e "demo", de acordo com o nível da API. Para fazer isso, o Plug-in do Android para Gradle permite criar diversos grupos de variações de produto como dimensões de variação.
Ao criar o app, para criar a variante de build final, o Gradle combina a configuração de uma variação de produto de cada dimensão de variações definida a uma configuração de um tipo de build. O Gradle não combina variações de produto pertencentes à mesma dimensão.
O exemplo de código abaixo usa a propriedade
flavorDimensions
para criar uma dimensão
de variações "mode" (modo) para agrupar as variações de produto "full" (completa) e "demo" (demonstração) e uma dimensão
"api" para agrupar as configurações das variações de produto, de acordo com o nível da API:
Kotlin
android { ... buildTypes { getByName("debug") {...} getByName("release") {...} } // Specifies the flavor dimensions you want to use. The order in which you // list the dimensions determines their priority, from highest to lowest, // when Gradle merges variant sources and configurations. You must assign // each product flavor you configure to one of the flavor dimensions. flavorDimensions += listOf("api", "mode") productFlavors { create("demo") { // Assigns this product flavor to the "mode" flavor dimension. dimension = "mode" ... } create("full") { dimension = "mode" ... } // Configurations in the "api" product flavors override those in "mode" // flavors and the defaultConfig block. Gradle determines the priority // between flavor dimensions based on the order in which they appear next // to the flavorDimensions property, with the first dimension having a higher // priority than the second, and so on. create("minApi24") { dimension = "api" minSdk = 24 // To ensure the target device receives the version of the app with // the highest compatible API level, assign version codes in increasing // value with API level. versionCode = 30000 + (android.defaultConfig.versionCode ?: 0) versionNameSuffix = "-minApi24" ... } create("minApi23") { dimension = "api" minSdk = 23 versionCode = 20000 + (android.defaultConfig.versionCode ?: 0) versionNameSuffix = "-minApi23" ... } create("minApi21") { dimension = "api" minSdk = 21 versionCode = 10000 + (android.defaultConfig.versionCode ?: 0) versionNameSuffix = "-minApi21" ... } } } ...
Groovy
android { ... buildTypes { debug {...} release {...} } // Specifies the flavor dimensions you want to use. The order in which you // list the dimensions determines their priority, from highest to lowest, // when Gradle merges variant sources and configurations. You must assign // each product flavor you configure to one of the flavor dimensions. flavorDimensions "api", "mode" productFlavors { demo { // Assigns this product flavor to the "mode" flavor dimension. dimension "mode" ... } full { dimension "mode" ... } // Configurations in the "api" product flavors override those in "mode" // flavors and the defaultConfig block. Gradle determines the priority // between flavor dimensions based on the order in which they appear next // to the flavorDimensions property, with the first dimension having a higher // priority than the second, and so on. minApi24 { dimension "api" minSdkVersion 24 // To ensure the target device receives the version of the app with // the highest compatible API level, assign version codes in increasing // value with API level. versionCode 30000 + android.defaultConfig.versionCode versionNameSuffix "-minApi24" ... } minApi23 { dimension "api" minSdkVersion 23 versionCode 20000 + android.defaultConfig.versionCode versionNameSuffix "-minApi23" ... } minApi21 { dimension "api" minSdkVersion 21 versionCode 10000 + android.defaultConfig.versionCode versionNameSuffix "-minApi21" ... } } } ...
O número de variantes de build que o Gradle cria é igual ao produto do número de variações presentes em cada dimensão de variações e do número de tipos de build que você configura. Quando o Gradle dá nome a cada variante de build ou artefato correspondente, as variações de produto que pertencem à dimensão de maior prioridade aparecem primeiro, seguidas das outras de menor prioridade e, por último, dos tipos de build.
Usando a configuração do build anterior como exemplo, o Gradle cria um total de 12 variantes de build com este esquema de nomenclatura:
- Variante de build:
[minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
- APK correspondente:
app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
- Por exemplo:
- Variante de build:
minApi24DemoDebug
- APK correspondente:
app-minApi24-demo-debug.apk
Além dos diretórios de conjunto de origem que você pode criar para cada
variante de build e variação de produto, também é possível criar diretórios de conjunto de origem
para cada combinação de variações de produto. Por exemplo, é possível criar
e adicionar origens Java ao diretório src/demoMinApi24/java/
,
e o Gradle usa essas origens só para criar uma variante que combina
duas dessas variações de produto.
Os conjuntos de origem criados para combinações de variação de produto têm prioridade maior do que os que pertencem a cada variação de produto. Para saber mais sobre conjuntos de origem e como o Gradle combina recursos, leia a seção sobre como Criar conjuntos de origem.
Filtrar variantes
O Gradle cria uma variante de build para cada combinação possível das variações
de produto e tipos de build que você configurar. No entanto, é possível que algumas
variantes de build sejam desnecessárias ou não façam sentido no
contexto do seu projeto. Para remover determinadas configurações de variantes de build,
crie um filtro de variantes no arquivo build.gradle.kts
do módulo.
Usando a configuração do build da seção anterior como exemplo,
suponha que você planeje oferecer suporte somente ao nível 23 da API e mais recentes para a versão de
demonstração do app. Você pode usar o bloco
variantFilter
para filtrar todas as configurações
de variantes de build que combinam as variações de produtos "minApi21" e "demo":
Kotlin
android { ... buildTypes {...} flavorDimensions += listOf("api", "mode") productFlavors { create("demo") {...} create("full") {...} create("minApi24") {...} create("minApi23") {...} create("minApi21") {...} } } androidComponents { beforeVariants { variantBuilder -> // To check for a certain build type, use variantBuilder.buildType == "<buildType>" if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) { // Gradle ignores any variants that satisfy the conditions above. variantBuilder.enable = false } } } ...
Groovy
android { ... buildTypes {...} flavorDimensions "api", "mode" productFlavors { demo {...} full {...} minApi24 {...} minApi23 {...} minApi21 {...} } variantFilter { variant -> def names = variant.flavors*.name // To check for a certain build type, use variant.buildType.name == "<buildType>" if (names.contains("minApi21") && names.contains("demo")) { // Gradle ignores any variants that satisfy the conditions above. setIgnore(true) } } } ...
Depois que você adiciona um filtro de variantes à configuração do build e clica em Sync Now na barra de notificações, o Gradle ignora aquelas que atendem às condições especificadas. As variantes de build não aparecem mais no menu quando você clica em Build > Select Build Variant na barra de menus ou em Build Variants na barra da janela de ferramentas.
Criar conjuntos de origem
Por padrão, o Android Studio criará o
conjunto de origem de main/
e diretórios para
tudo o que você quiser compartilhar entre suas variantes de build. No entanto, você
pode criar novos conjuntos de origem para determinar exatamente quais arquivos o Gradle precisa compilar e
empacotar para tipos de build, variações de produtos (e combinações de
variações de produtos ao usar dimensões
de variações) e variantes de build específicas.
Por exemplo, é possível definir a funcionalidade básica
do conjunto de origem main/
e usar os conjuntos
de origem de uma variação de produto para mudar o aspecto visual do seu app para diferentes clientes.
Além disso, só é possível incluir permissões especiais e recursos de geração de registros para variantes de build
que usam o tipo de build de depuração.
O Gradle espera que os arquivos e diretórios dos conjuntos de origem sejam organizados de alguma forma,
assim como acontece com o conjunto de origem main/
. Por exemplo, o Gradle
espera que os arquivos de classe Kotlin ou Java específicos do seu tipo de build "debug" estejam
localizados nos diretórios src/debug/kotlin/
ou src/debug/java/
.
O Plug-in do Android para Gradle fornece uma tarefa útil do Gradle que mostra como organizar seus arquivos por tipo de build, variação de produto e variante de build. O exemplo de saída da tarefa abaixo descreve onde o Gradle espera encontrar determinados arquivos para o tipo de build "debug":
------------------------------------------------------------ Project :app ------------------------------------------------------------ ... debug ---- Compile configuration: debugCompile build.gradle name: android.sourceSets.debug Java sources: [app/src/debug/java] Kotlin sources: [app/src/debug/kotlin, app/src/debug/java] Manifest file: app/src/debug/AndroidManifest.xml Android resources: [app/src/debug/res] Assets: [app/src/debug/assets] AIDL sources: [app/src/debug/aidl] RenderScript sources: [app/src/debug/rs] JNI sources: [app/src/debug/jni] JNI libraries: [app/src/debug/jniLibs] Java-style resources: [app/src/debug/resources]
Para conferir essa saída, faça o seguinte:
- Na barra da janela de ferramentas, clique em Gradle.
Vá até MyApplication > Tasks > android e clique duas vezes em sourceSets.
Para encontrar a pasta Tasks, é necessário permitir que o Gradle crie a lista de tarefas durante a sincronização. Para isso, siga estas etapas:
- Clique em File > Settings > Experimental (Android Studio > Settings > Experimental no macOS).
- Desmarque a opção Do not build Gradle task list during Gradle sync.
- Depois que o Gradle executa a tarefa, a janela Run é aberta para mostrar a saída.
Observação: a saída da tarefa também mostra como organizar conjuntos de origem
para os arquivos que você quer usar para executar testes com o app, como os
conjuntos de origem de teste
test/
e androidTest/
.
Ao criar uma nova variante de build, o Android Studio não cria os diretórios de conjuntos de origem, mas oferece algumas opções que podem ajudar. Por
exemplo, para criar apenas o diretório java/
para seu
tipo de build "debug":
- Abra o painel Project e selecione a visualização Project no menu localizado na parte de cima do painel.
- Navegue para
MyProject/app/src/
. - Clique com o botão direito do mouse no diretório
src
e selecione New > Directory. - No menu em Gradle Source Sets, selecione full/java.
- Pressione Enter.
O Android Studio cria um diretório do conjunto de origem para seu tipo de build de depuração e,
em seguida, cria o diretório java/
dentro dele. Como alternativa,
o Android Studio pode criar diretórios quando você adiciona um novo arquivo ao
projeto para uma variante de build específica.
Por exemplo, para criar um arquivo Values XML para o tipo de build "debug":
- No painel Project, clique com o botão direito do mouse no diretório
src
e selecione New > XML > Values XML File. - Insira o nome do arquivo XML ou mantenha o nome padrão.
- No menu ao lado de Target Source Set, selecione debug.
- Clique em Finish.
Como o tipo de build "debug" foi especificado como o conjunto de origem de destino, o Android Studio criará os diretórios necessários automaticamente ao criar o arquivo XML. A estrutura de diretórios resultante é semelhante à Figura 1.
Os conjuntos de origem ativos têm um indicador verde no ícone para mostrar que estão ativos. O
conjunto de origem debug
tem o sufixo [main]
para mostrar que vai ser mesclado
ao conjunto de origem main
.
Com o mesmo procedimento, também é possível criar diretórios de conjuntos de origem para
variações de produtos, como src/demo/
, e para variantes de build, como
src/demoDebug/
. Além disso, você pode criar conjuntos de origem para testes, voltados para variantes de build específicas, como em src/androidTestDemoDebug/
. Para saber mais, consulte
testes de conjuntos de origem.
Mudar as configurações do conjunto de origem padrão
Se você tem origens que não são organizadas na estrutura padrão de arquivos de conjunto
de origem que o Gradle espera, como descrito na seção sobre como
criar conjuntos de origem, use o bloco
sourceSets
para mudar o local em que o Gradle procura
arquivos de cada componente de um conjunto de origem.
O bloco sourceSets
precisa estar
no bloco android
. Não é necessário realocar os
arquivos de origem, mas apenas fornecer ao Gradle os caminhos (em relação ao arquivo
build.gradle.kts
no nível do módulo) em que ele
pode encontrar arquivos de cada componente do conjunto de origem. Para saber quais componentes podem ser
configurados e se você pode mapeá-los em diversos arquivos ou diretórios,
consulte a referência da API do Plugin do Android para Gradle.
O exemplo de código abaixo mapeia origens do diretório app/other/
em determinados componentes do conjunto de origem main
e muda o
diretório raiz do conjunto de origem androidTest
:
Kotlin
android { ... // Encapsulates configurations for the main source set. sourceSets.getByName("main") { // Changes the directory for Java sources. The default directory is // 'src/main/java'. java.setSrcDirs(listOf("other/java")) // If you list multiple directories, Gradle uses all of them to collect // sources. Because Gradle gives these directories equal priority, if // you define the same resource in more than one directory, you receive an // error when merging resources. The default directory is 'src/main/res'. res.setSrcDirs(listOf("other/res1", "other/res2")) // Note: Avoid specifying a directory that is a parent to one // or more other directories you specify. For example, avoid the following: // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings'] // Specify either only the root 'other/res1' directory or only the // nested 'other/res1/layouts' and 'other/res1/strings' directories. // For each source set, you can specify only one Android manifest. // By default, Android Studio creates a manifest for your main source // set in the src/main/ directory. manifest.srcFile("other/AndroidManifest.xml") ... } // Create additional blocks to configure other source sets. sourceSets.getByName("androidTest") { // If all the files for a source set are located under a single root // directory, you can specify that directory using the setRoot property. // When gathering sources for the source set, Gradle looks only in locations // relative to the root directory you specify. For example, after applying the // configuration below for the androidTest source set, Gradle looks for Java // sources only in the src/tests/java/ directory. setRoot("src/tests") ... } } ...
Groovy
android { ... sourceSets { // Encapsulates configurations for the main source set. main { // Changes the directory for Java sources. The default directory is // 'src/main/java'. java.srcDirs = ['other/java'] // If you list multiple directories, Gradle uses all of them to collect // sources. Because Gradle gives these directories equal priority, if // you define the same resource in more than one directory, you receive an // error when merging resources. The default directory is 'src/main/res'. res.srcDirs = ['other/res1', 'other/res2'] // Note: Avoid specifying a directory that is a parent to one // or more other directories you specify. For example, avoid the following: // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings'] // Specify either only the root 'other/res1' directory or only the // nested 'other/res1/layouts' and 'other/res1/strings' directories. // For each source set, you can specify only one Android manifest. // By default, Android Studio creates a manifest for your main source // set in the src/main/ directory. manifest.srcFile 'other/AndroidManifest.xml' ... } // Create additional blocks to configure other source sets. androidTest { // If all the files for a source set are located under a single root // directory, you can specify that directory using the setRoot property. // When gathering sources for the source set, Gradle looks only in locations // relative to the root directory you specify. For example, after applying the // configuration below for the androidTest source set, Gradle looks for Java // sources only in the src/tests/java/ directory. setRoot 'src/tests' ... } } } ...
Observe que um diretório de origem só pode pertencer a um único conjunto de origem. Por exemplo, não é possível usar as
mesmas origens de teste para os conjuntos de origem test
e androidTest
. Isso
ocorre porque o Android Studio cria módulos do IntelliJ separados para cada conjunto de origem e não oferece suporte
a raízes de conteúdo duplicado nesses conjuntos.
Criar com conjuntos de origem
Você pode usar os diretórios dos conjuntos de origem para armazenar o código e os recursos que quer empacotar apenas com determinadas configurações. Por exemplo, se você estiver criando a variante de build "demoDebug", que é o produto da combinação de uma variação de produto "demo" e um tipo de build "debug", o Gradle vai examinar esses diretórios e atribuir as seguintes prioridades a eles:
-
src/demoDebug/
(conjunto de origem da variante de build) -
src/debug/
(conjunto de origem de tipo de build) -
src/demo/
(conjunto de origem de variação de produto) -
src/main/
(conjunto de origem principal)
Os conjuntos de origem criados para combinações de variação de produto precisam incluir todas as dimensões de variação. Por exemplo, o conjunto de origem da variante de build precisa ser a combinação do tipo de build e de todas as dimensões de variações. Não há suporte à mesclagem de códigos e recursos envolvendo pastas que abrangem várias, dimensões de variações, mas não todas elas.
Se você combina diversas variações de
produto, a prioridade entre elas é determinada pela
dimensão a que pertencem. Ao classificar as dimensões de variação com a
propriedade
android.flavorDimensions
, as variações de produto que
pertencem à primeira dimensão listada têm maior prioridade do que
aquelas que pertencem à segunda dimensão e assim por diante. Além disso,
os conjuntos de origem que você cria para combinações de variação de produto têm prioridade maior
em relação aos conjuntos de origem que pertencem a cada variação de produto.
A ordem de prioridade determina qual conjunto de origem tem maior prioridade
quando o Gradle combina código e recursos. Como o diretório demoDebug/
do conjunto de origem provavelmente contém arquivos específicos dessa variante de
build, caso demoDebug/
contenha um arquivo também definido em
debug/
, o Gradle usará o arquivo do conjunto de origem
de demoDebug/
. De forma semelhante, o Gradle atribui aos arquivos que se encontram nos conjuntos de origem de tipo de build e de variação de produto
uma prioridade maior do que os mesmos arquivos presentes em main/
.
O Gradle considera essa ordem de prioridade ao aplicar estas regras de build:
- Todo código-fonte dos diretórios
kotlin/
oujava/
é compilado junto para gerar uma única saída.Observação: para uma variante de build específica, o Gradle vai gerar um erro de build se encontrar dois ou mais diretórios de conjuntos de origem que tenham definido a mesma classe Kotlin ou Java. Por exemplo, ao criar um app de depuração, não é possível definir
src/debug/Utility.kt
esrc/main/Utility.kt
, porque o Gradle analisa esses diretórios durante o processo de build e gera um erro "duplicate class" (classe duplicada). Caso você queira ter diferentes versões doUtility.kt
para diferentes tipos de build, cada tipo de build precisa definir a própria versão do arquivo e não a incluir no conjunto de origemmain/
. - Os manifestos são combinados em um único. A prioridade é concedida na mesma ordem da lista do exemplo anterior. Ou seja, as configurações de manifesto para um tipo de build substituem as configurações de manifesto para uma variação de produto e assim por diante. Para saber mais, leia sobre a combinação de manifestos.
- Arquivos dos diretórios
values/
são combinados. Caso dois arquivos tenham o mesmo nome, por exemplo,strings.xml
, a prioridade será concedida na mesma ordem da lista do exemplo acima. Ou seja, os valores definidos em um arquivo no conjunto de origem do tipo de build substituem os valores definidos no mesmo arquivo em uma variação de produto e assim por diante. - Os recursos dos diretórios
res/
easset/
são empacotados juntos. Se há recursos com o mesmo nome definidos em dois ou mais conjuntos de origem, a prioridade é concedida na ordem da lista do exemplo anterior. - O Gradle atribui a menor prioridade aos recursos e manifestos incluídos com dependências de módulo de biblioteca quando o app é criado.
Declarar dependências
Você pode configurar uma dependência para uma variante de build ou um
conjunto de origem de teste específico,
usando o nome da variante ou do conjunto de origem de teste como prefixo antes da
palavra-chave Implementation
, como mostrado no exemplo abaixo.
Kotlin
dependencies { // Adds the local "mylibrary" module as a dependency to the "free" flavor. "freeImplementation"(project(":mylibrary")) // Adds a remote binary dependency only for local tests. testImplementation("junit:junit:4.12") // Adds a remote binary dependency only for the instrumented test APK. androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1") }
Groovy
dependencies { // Adds the local "mylibrary" module as a dependency to the "free" flavor. freeImplementation project(":mylibrary") // Adds a remote binary dependency only for local tests. testImplementation 'junit:junit:4.12' // Adds a remote binary dependency only for the instrumented test APK. androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1' }
Para mais informações sobre dependências, consulte Adicionar dependências de build.
Usar gerenciamento de dependências com reconhecimento de variantes
O Plug-in do Android para Gradle 3.0.0 e versões mais recentes inclui um novo mecanismo de dependência que faz a correspondência automática
de variantes ao consumir uma biblioteca. Isso significa que a variante debug
de um app consome
automaticamente a variante debug
de uma biblioteca e assim por diante. Isso também funciona ao usar
variações: a variante freeDebug
de um app vai consumir a variante freeDebug
de uma biblioteca.
Para que o plug-in estabeleça correspondências precisas entre as variantes, é necessário fornecer substitutos de correspondência, conforme descrito na próxima seção, para casos em que uma correspondência direta não é possível.
Por exemplo, suponha que seu app configure um tipo de build chamado "staging" (de preparo), mas uma das dependências de biblioteca dele não. Quando o plug-in tentar criar a versão "staging" do app, ele não saberá qual versão da biblioteca usar, e você vai encontrar uma mensagem de erro semelhante a esta:
Error:Failed to resolve: Could not resolve project :mylibrary. Required by: project :app
Resolver erros de build relacionados à correspondência de variantes
O plug-in inclui elementos DSL para ajudar a controlar como o Gradle soluciona situações em que uma correspondência direta de variante entre um app e uma dependência não é possível.
Confira abaixo uma lista de problemas relacionados à correspondência de dependências com reconhecimento de variantes e como resolvê-los usando as propriedades de DSL.O app inclui um tipo de build que não está presente em uma dependência de biblioteca.
Por exemplo, o app inclui um tipo de build "staging" (de preparo), mas uma dependência inclui somente os tipos de build "debug" (depuração) e "release" (lançamento).
Observe que não há problemas quando uma dependência de biblioteca inclui um tipo de build que o app não tem. Isso ocorre porque o plug-in nunca solicita esse tipo de build da dependência.
Use o elemento
matchingFallbacks
para especificar correspondências alternativas para determinados tipos de build, conforme mostrado abaixo.Kotlin
// In the app's build.gradle.kts file. android { buildTypes { getByName("debug") {} getByName("release") {} create("staging") { // Specifies a sorted list of fallback build types that the // plugin can try to use when a dependency does not include a // "staging" build type. You may specify as many fallbacks as you // like, and the plugin selects the first build type that's // available in the dependency. matchingFallbacks += listOf("debug", "qa", "release") } } }
Groovy
// In the app's build.gradle file. android { buildTypes { debug {} release {} staging { // Specifies a sorted list of fallback build types that the // plugin can try to use when a dependency does not include a // "staging" build type. You may specify as many fallbacks as you // like, and the plugin selects the first build type that's // available in the dependency. matchingFallbacks = ['debug', 'qa', 'release'] } } }
Para determinada dimensão de variações que existe no app e na dependência de biblioteca, o app inclui variações que a biblioteca não tem.
Por exemplo, tanto o app quanto as dependências de biblioteca incluem uma dimensão de variação "tier" (nível). Entretanto, a dimensão "tier" no app inclui variações "free" (sem custo) e "paid" (paga), mas uma dependência inclui somente variações "demo" (demonstração) e "paid" (paga) para a mesma dimensão.
Observe que, para determinada dimensão de variação que existe no app e nas dependências de biblioteca, não há problema quando uma biblioteca inclui uma variação de produto que o app não tem. Isso ocorre porque o plug-in nunca solicita essa variação da dependência.
Use
matchingFallbacks
para especificar correspondências alternativas para uma variação de produto "free" do app, conforme mostrado aqui:Kotlin
// In the app's build.gradle.kts file. android { defaultConfig{ // Don't configure matchingFallbacks in the defaultConfig block. // Instead, specify fallbacks for a given product flavor in the // productFlavors block, as shown below. } flavorDimensions += "tier" productFlavors { create("paid") { dimension = "tier" // Because the dependency already includes a "paid" flavor in its // "tier" dimension, you don't need to provide a list of fallbacks // for the "paid" flavor. } create("free") { dimension = "tier" // Specifies a sorted list of fallback flavors that the plugin // can try to use when a dependency's matching dimension does // not include a "free" flavor. Specify as many // fallbacks as you like; the plugin selects the first flavor // that's available in the dependency's "tier" dimension. matchingFallbacks += listOf("demo", "trial") } } }
Groovy
// In the app's build.gradle file. android { defaultConfig{ // Don't configure matchingFallbacks in the defaultConfig block. // Instead, specify fallbacks for a given product flavor in the // productFlavors block, as shown below. } flavorDimensions 'tier' productFlavors { paid { dimension 'tier' // Because the dependency already includes a "paid" flavor in its // "tier" dimension, you don't need to provide a list of fallbacks // for the "paid" flavor. } free { dimension 'tier' // Specifies a sorted list of fallback flavors that the plugin // can try to use when a dependency's matching dimension does // not include a "free" flavor. Specify as many // fallbacks as you like; the plugin selects the first flavor // that's available in the dependency's "tier" dimension. matchingFallbacks = ['demo', 'trial'] } } }
Uma dependência de biblioteca inclui uma dimensão de variação que o app não tem.
Por exemplo, uma dependência de biblioteca inclui variações para uma dimensão "minApi", mas o app inclui variações apenas para a dimensão "tier" (nível). Quando você quiser criar a versão "freeDebug" (depuração sem custo financeiro) do app, o plug-in não vai saber se precisa usar a versão "minApi23Debug" ou "minApi18Debug" da dependência.
Observe que não há problemas quando seu app inclui uma dimensão de variação que uma dependência de biblioteca não tem. Isso ocorre porque o plug-in faz a correspondência de variações apenas nas dimensões que existem na dependência. Por exemplo, se uma dependência não incluir uma dimensão para ABIs, a versão "freeX86Debug" do app simplesmente vai usar a versão "freeDebug" da dependência.
Use
missingDimensionStrategy
no blocodefaultConfig
para especificar a variação padrão a ser selecionada pelo plug-in de cada dimensão ausente, conforme mostrado no exemplo abaixo. Você também pode substituir as seleções no blocoproductFlavors
, de forma que cada variação possa especificar uma estratégia de correspondência diferente para uma dimensão ausente.Kotlin
// In the app's build.gradle.kts file. android { defaultConfig{ // Specifies a sorted list of flavors that the plugin can try to use from // a given dimension. This tells the plugin to select the "minApi18" flavor // when encountering a dependency that includes a "minApi" dimension. // You can include additional flavor names to provide a // sorted list of fallbacks for the dimension. missingDimensionStrategy("minApi", "minApi18", "minApi23") // Specify a missingDimensionStrategy property for each // dimension that exists in a local dependency but not in your app. missingDimensionStrategy("abi", "x86", "arm64") } flavorDimensions += "tier" productFlavors { create("free") { dimension = "tier" // You can override the default selection at the product flavor // level by configuring another missingDimensionStrategy property // for the "minApi" dimension. missingDimensionStrategy("minApi", "minApi23", "minApi18") } create("paid") {} } }
Groovy
// In the app's build.gradle file. android { defaultConfig{ // Specifies a sorted list of flavors that the plugin can try to use from // a given dimension. This tells the plugin to select the "minApi18" flavor // when encountering a dependency that includes a "minApi" dimension. // You can include additional flavor names to provide a // sorted list of fallbacks for the dimension. missingDimensionStrategy 'minApi', 'minApi18', 'minApi23' // Specify a missingDimensionStrategy property for each // dimension that exists in a local dependency but not in your app. missingDimensionStrategy 'abi', 'x86', 'arm64' } flavorDimensions 'tier' productFlavors { free { dimension 'tier' // You can override the default selection at the product flavor // level by configuring another missingDimensionStrategy property // for the 'minApi' dimension. missingDimensionStrategy 'minApi', 'minApi23', 'minApi18' } paid {} } }
Para saber mais, consulte matchingFallbacks
e missingDimensionStrategy
na referência de DSL do Plug-in do Android para Gradle.
Definir configurações de assinatura
O Gradle só assina o APK ou AAB do build de lançamento, se você define explicitamente uma configuração de assinatura para esse build. Se você ainda não tem uma chave de assinatura, gere uma chave de upload e um keystore usando o Android Studio.
Para definir manualmente as configurações de assinatura para seu tipo de build de lançamento usando as configurações de build do Gradle, siga estas etapas:
- Crie um keystore. Um keystore é um arquivo binário que contém um conjunto de chaves privadas. É necessário manter o keystore em um local seguro e protegido.
- Crie uma chave privada. Uma chave privada é usada para assinar seu app para distribuição e nunca é incluída no app nem divulgada a terceiros não autorizados.
-
Adicione a configuração de assinatura ao arquivo
build.gradle.kts
do módulo:Kotlin
... android { ... defaultConfig {...} signingConfigs { create("release") { storeFile = file("myreleasekey.keystore") storePassword = "password" keyAlias = "MyReleaseKey" keyPassword = "password" } } buildTypes { getByName("release") { ... signingConfig = signingConfigs.getByName("release") } } }
Groovy
... android { ... defaultConfig {...} signingConfigs { release { storeFile file("myreleasekey.keystore") storePassword "password" keyAlias "MyReleaseKey" keyPassword "password" } } buildTypes { release { ... signingConfig signingConfigs.release } } }
Observação: incluir as senhas da chave de lançamento e do keystore dentro do arquivo de build não é uma prática de segurança recomendada. Em vez disso, configure o arquivo do build para extrair essas senhas de variáveis de ambiente ou para que o processo de build as solicite.
Para receber essas senhas de variáveis de ambiente:
Kotlin
storePassword = System.getenv("KSTOREPWD") keyPassword = System.getenv("KEYPWD")
Groovy
storePassword System.getenv("KSTOREPWD") keyPassword System.getenv("KEYPWD")
Como alternativa, carregue o keystore de um arquivo de propriedades local. Por motivos de segurança, não adicione esse arquivo ao controle de origem. Em vez disso, configure-o localmente para cada desenvolvedor. Para saber mais, consulte Remover informações de assinatura dos arquivos de build.
Após a conclusão desse processo, você poderá distribuir seu app e publicá-lo no Google Play.
Aviso: mantenha o keystore e a chave privada em um local seguro e proteja os backups deles. Se você usar a Assinatura de apps do Google Play e perder sua chave de upload, poderá solicitar uma redefinição usando o Play Console. Se você estiver publicando um app sem a Assinatura de apps do Google Play (para apps criados antes de agosto de 2021) e perder a chave de assinatura do app, não será possível publicar nenhuma atualização, porque você precisa sempre assinar todas as versões do app com a mesma chave.
Assinatura de apps do Wear OS
Ao publicar apps para Wear OS, o APK do relógio e o APK opcional do smartphone precisam ser assinados com a mesma chave. Para mais informações sobre o empacotamento e a assinatura de apps do Wear OS, consulte Empacotar e distribuir apps do Wear.