Configurar variantes de build

Esta página usa as informações fornecidas na Visão geral da configuração do seu build para mostrar 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 uma versão sem custo financeiro do app, com uma quantidade limitada de conteúdo, e uma versão paga com mais conteúdo. 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 configurados nos seus tipos de build e variações de produtos. Apesar de as variantes de build não serem configuradas diretamente, é necessário configurar os tipos de build e as variações de produto que compõem as variantes.

Por exemplo, uma variação de produto "demo" (demonstração) pode especificar diferentes 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 compilação resultante é a versão "demoDebug" do seu app, contendo uma combinação das configurações e dos recursos incluídos 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 arquivo build.gradle do módulo dentro do bloco android. 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 determinadas configurações. O exemplo a seguir especifica um applicationIdSuffix (link em inglês) para o tipo de build de depuração e configura um tipo ded build "staging" (de preparo), que é inicializado usando as configurações do tipo de build de depuração.

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 allows you to 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"
        }
    }
}

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 allows you to 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"
        }
    }
}

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 isso, 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 será exibida com uma descrição do problema.

Para saber mais sobre todas as propriedades que podem ser configuradas com tipos de build, leia a referência de DSL de tipos de build.

Configurar variações de produtos

A criação de variações de produtos é semelhante à de tipos de build: basta adicioná-las ao bloco productFlavors na configuração da compilação e aplicar as definições que você quiser. As variações de produtos são compatíveis com as mesmas propriedades que defaultConfig. Isso 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 é 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. Atribua todas as variações a uma dimensão; caso contrário, você receberá o erro de compilação mostrado abaixo. 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.

  Error:All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

A amostra de código a seguir cria uma dimensão de variações chamada "versão" e adiciona variações de produto "demo" (demonstração) e "full" (completa). Essas variações fornecem os próprios applicationIdSuffix e versionNameSuffix:

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"
        }
    }
}

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"
        }
    }
}

Observação: para apps legados que são distribuídos usando APKs no Google Play (criados antes de agosto de 2021), para distribuí-los usando o suporte para vários APKs no Google Play, atribua o mesmo valor applicationId a todas as variantes e dê um versionCode diferente a cada uma. 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 for concluída, o Gradle criará as variantes de compilação automaticamente com base nos tipos de build e nas variações de produto e nomeará as variantes 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 criará as seguintes variantes de compilação:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

É possível mudar a variante de build para aquela que você quer criar e executar. Para fazer isso, basta acessar Build > Select Build Variant e selecionar uma opção no menu suspenso. Para começar a personalizar cada variante de build com os próprios recursos, você precisará saber como criar e gerenciar conjuntos de origem.

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, como mostrado abaixo. No entanto, se você quiser criar versões diferentes do app para aparecerem como listagens separadas na Google Play Store, como uma versão "free" (sem custo financeiro) e uma "pro" (avançada), será necessário criar variantes de build separadas, cada uma com um ID de aplicativo diferente. Nesse caso, cada variante de build precisa ser definida como uma variação de produto diferente. 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 mostrado abaixo:

Groovy

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

Kotlin

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("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:

Groovy

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

Kotlin

android {
    ...
    buildTypes {
        getByName("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. Para apps legados que são distribuídos usando APKs no Google Play (criados antes de agosto de 2021), se você quiser usar a mesma página "Detalhes do app" para distribuir vários APKs voltados a diferentes configurações de dispositivo (como o nível da API), você precisa usar o mesmo ID do aplicativo para cada variante de build, mas dar a cada APK um versionCode 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.

Cuidado: para compatibilidade com Ferramentas do SDK anteriores, se você não definir a propriedade applicationId no arquivo build.gradle, as ferramentas de compilação usarão o nome do pacote do arquivo AndroidManifest.xml como o ID do aplicativo. Nesse caso, a refatoração do nome de pacote também muda o ID do aplicativo.

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 uma compilaçã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 vários grupos de variações de produtos, chamados de dimensão de variações. Ao criar o app, para criar a variante de compilação 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 que pertençam à mesma dimensão.

O exemplo de código a seguir 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:

Groovy

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its 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 above--the first dimension has 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. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      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"
      ...
    }
  }
}
...

Kotlin

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its 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 above--the first dimension has 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. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      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"
      ...
    }
  }
}
...

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 acima como exemplo, o Gradle cria um total de 12 variantes de build com o seguinte esquema de nomenclatura:

Variante de compilação: [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 compilar 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 compilação 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 sejam desnecessárias ou não façam sentido no contexto do seu projeto. Você pode remover determinadas configurações de variante de build criando um filtro de variantes no arquivo build.gradle do módulo.

Usando a configuração do build da seção anterior como exemplo, suponha que você planeje oferecer compatibilidade somente com os níveis da API 23 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":

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)
      }
  }
}
...

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.enabled = false
        }
    }
}
...

Depois de adicionar um filtro de variante à configuração do build e clicar em Sync Now na barra de notificações, o Gradle ignorará todas as variantes de build que atenderem às condições especificadas. Assim, elas não serão mais exibidas no menu suspenso quando você clicar em Build > Select Build Variant na barra de menus (ou 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íficos. 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, é possível incluir permissões especiais e recursos de geração de registros somente para variantes de compilação que usam o tipo de build de depuração.

O Gradle espera que você organize os arquivos e diretórios dos conjuntos de origem de uma forma específica, semelhante ao conjunto de origem main/. Por exemplo, o Gradle espera que os arquivos de classe Java específicos do seu tipo de build "debug" estejam no diretório src/debug/java/.

O plug-in do Android para Gradle disponibiliza uma tarefa muito útil do Gradle, que mostra como organizar seus arquivos por tipo de build, variação de produto e variante de compilação. A amostra de saída da tarefa a seguir descreve onde o Gradle espera encontrar determinados arquivos para o tipo de compilação "debug":

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: compile
build.gradle name: android.sourceSets.debug
Java sources: [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 ver essa saída, faça o seguinte:

  1. Clique em Gradle no lado direito da janela do ambiente de desenvolvimento integrado.
  2. Vá até MyApplication > Tasks > android e clique duas vezes em sourceSets. Depois que o Gradle executar a tarefa, a janela Run será aberta para exibir a saída.
  3. Caso a exibição não esteja no modo de texto mostrado acima, clique em Toggle view no lado esquerdo da janela Run.

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":

  1. Abra o painel Project e selecione a visualização Project no menu suspenso na parte superior do painel.
  2. Navegue para MyProject/app/src/.
  3. Clique com o botão direito do mouse no diretório src e selecione New > Folder > Java Folder.
  4. No menu suspenso ao lado de Target Source Set, selecione debug.
  5. Clique em Finish.

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. Uma alternativa é fazer com que o Android Studio crie diretórios quando você adicionar um novo arquivo no seu projeto para uma variante de compilação específica. Por exemplo, para criar um arquivo Values XML para o tipo de build "debug":

  1. No mesmo painel Project, clique com o botão direito do mouse no diretório src e selecione New > XML > Values XML File.
  2. Insira o nome do arquivo XML ou mantenha o nome padrão.
  3. No menu suspenso ao lado de Target Source Set, selecione debug.
  4. 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 será semelhante à Figura 2.

Figura 2. Novos diretórios de conjuntos de origem para o tipo de build de depuração.

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 acima 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. Não é necessário realocar os arquivos; basta fornecer ao Gradle os caminhos, relacionados ao arquivo build.gradle no nível do módulo, e o Gradle encontrará os 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 Android Gradle Plugin.

O exemplo de código a seguir 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.

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 get an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: You should avoid specifying a directory which 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']
      // You should 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'
      ...
    }
  }
}
...

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("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 get an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs("other/res1", "other/res2")

    // Note: You should avoid specifying a directory which 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']
    // You should 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")
      ...
  }
}
...

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 compilação "demoDebug", que é o produto da combinação de uma variação de produto "demo" e um tipo de build "debug", o Gradle examinará esses diretórios e atribuirá as seguintes prioridades a eles:

  1. src/demoDebug/ (conjunto de origem de variante de compilação)
  2. src/debug/ (conjunto de origem de tipo de build)
  3. src/demo/ (conjunto de origem de variação de produto)
  4. src/main/ (conjunto de origem principal)

Observação: se você combinar diversas variações de produto, a prioridade entre elas será 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 terão maior prioridade do que as que pertencem à segunda dimensão e assim por diante. Além disso, os conjuntos de origem que você criar para combinações de variação de produto terão prioridade maior em relação aos conjuntos de origem que pertencem a cada variação de produto individualmente.

A ordem listada acima 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 compilação, 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 as seguintes regras de compilação:

  • Todo código-fonte dos diretórios java/ é compilado junto para gerar uma única saída.

    Observação: para uma variante de build específica, o Gradle gerará um erro de compilação se encontrar dois ou mais diretórios de conjuntos de origem que tenham definido a mesma classe Java. Por exemplo, ao criar um app de depuração, não é possível definir src/debug/Utility.java e src/main/Utility.java juntos. Isso porque o Gradle examina ambos os diretórios durante o processo de compilação e gera um erro "duplicate class" (classe duplicada). Caso queira ter diferentes versões do Utility.java para diferentes tipos de build, é possível fazer com que cada tipo de build defina a própria versão do arquivo e não a inclua no conjunto de origem main/.

  • Os manifestos são combinados em um único manifesto. A prioridade é atribuída na mesma ordem da lista acima. Ou seja, as configurações de manifesto para um tipo de build modificam 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.
  • De forma semelhante, os arquivos dos diretórios values/ são combinados. Caso dois arquivos tenham o mesmo nome, como strings.xml, a prioridade será concedida na mesma ordem da lista acima. Ou seja, os valores definidos em um arquivo no conjunto de origem do tipo de build modificam os valores definidos no mesmo arquivo em uma variação de produto e assim por diante.
  • Os recursos dos diretórios res/ e asset/ são empacotados juntos. Se houver recursos com o mesmo nome definidos em dois ou mais conjuntos de origem, a prioridade será concedida na ordem da lista acima.
  • Por fim, 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 específica ou um conjunto de origem de teste específico usando o nome da variante ou do conjunto como prefixo antes da palavra-chave Implementation, como mostrado no exemplo a seguir.

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.0.2'
}

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.0.2")
}

Para saber mais, consulte Adicionar dependências de build.

Usar o gerenciamento de dependências com reconhecimento de variantes

O plug-in do Android 3.0.0 e 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 consumirá a variante freeDebug de uma biblioteca.

Para que o plug-in estabeleça correspondências precisas entre as variantes, será necessário fornecer substitutos de correspondência aos casos em que uma correspondência direta é impossível. Verifique se o app configura 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ê verá uma mensagem de erro semelhante a esta:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

Resolver erros de compilação relacionados à correspondência de variantes

O plug-in inclui elementos DSL para ajudar a controlar como o Gradle solucionará situações em que uma correspondência direta de variante entre um app e uma dependência não é possível. Consulte a tabela para determinar qual propriedade de DSL você precisa usar a fim de solucionar certos erros de compilação relacionados à correspondência de dependências com reconhecimento de variantes.

Causa do erro de compilação Resolução

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á problema quando uma dependência de biblioteca inclui um tipo de build que o app não tem. Isso ocorre porque o plug-in simplesmente 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:

Groovy


// In the app's build.gradle file.
android {
    buildTypes {
        debug {}
        release {}
        staging {
            // Specifies a sorted list of fallback build types that the
            // plugin should 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']
        }
    }
}

Kotlin


// In the app's build.gradle file.
android {
    buildTypes {
        getByName("debug") {}
        getByName("release") {}
        create("staging") {
            // Specifies a sorted list of fallback build types that the
            // plugin should 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")
        }
    }
}

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 financeiro) 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 simplesmente 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 abaixo:

Groovy


// In the app's build.gradle file.
android {
    defaultConfig{
    // Do not configure matchingFallbacks in the defaultConfig block.
    // Instead, you must 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
            // should try to use when a dependency's matching dimension does
            // not include a "free" flavor. You may specify as many
            // fallbacks as you like, and the plugin selects the first flavor
            // that's available in the dependency's "tier" dimension.
            matchingFallbacks = ['demo', 'trial']
        }
    }
}

Kotlin


// In the app's build.gradle file.
android {
    defaultConfig{
    // Do not configure matchingFallbacks in the defaultConfig block.
    // Instead, you must 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
            // should try to use when a dependency's matching dimension does
            // not include a "free" flavor. You may specify as many
            // fallbacks as you like, and the plugin selects the first flavor
            // that's available in the dependency's "tier" dimension.
            matchingFallbacks += listOf("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). Então, quando você quiser criar a versão "freeDebug" (depuração sem custo financeiro) do app, o plug-in não saberá se precisa usar a versão "minApi23Debug" ou "minApi18Debug" da dependência.

Observe que não há problema quando o 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 usará a versão "freeDebug" da dependência.

Use missingDimensionStrategy no bloco defaultConfig para especificar a variação padrão que o plug-in precisa selecionar de cada dimensão ausente, conforme mostrado no exemplo abaixo. Você também pode modificar as seleções no bloco productFlavors, de forma que cada variação possa especificar uma estratégia de correspondência diferente para uma dimensão ausente.

Groovy


// In the app's build.gradle file.
android {
    defaultConfig{
    // Specifies a sorted list of flavors that the plugin should try to use from
    // a given dimension. The following tells the plugin that, when encountering
    // a dependency that includes a "minApi" dimension, it should select the
    // "minApi18" flavor. You can include additional flavor names to provide a
    // sorted list of fallbacks for the dimension.
    missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
    // You should 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 {}
    }
}

Kotlin


// In the app's build.gradle file.
android {
    defaultConfig{
    // Specifies a sorted list of flavors that the plugin should try to use from
    // a given dimension. The following tells the plugin that, when encountering
    // a dependency that includes a "minApi" dimension, it should select the
    // "minApi18" flavor. You can include additional flavor names to provide a
    // sorted list of fallbacks for the dimension.
    missingDimensionStrategy("minApi", "minApi18", "minApi23")
    // You should 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") {}
    }
}

Para mais informações, consulte matchingFallbacks e missingDimensionStrategy na referência de DSL do plug-in do Android.

Definir configurações de assinatura

O Gradle não assina o APK ou AAB do build de lançamento, a não ser que você defina explicitamente uma configuração de assinatura para esse build. Se você ainda não tiver uma chave de assinatura, veja como gerar 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:

  1. 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.
  2. 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.
  3. Adicione a configuração de assinatura ao arquivo build.gradle do módulo:

    Groovy

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

    Kotlin

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

Observação: incluir as senhas da chave de lançamento e do keystore dentro do arquivo de compilação não é uma prática de segurança recomendada. Como alternativa, você pode configurar o arquivo de compilação para receber essas senhas de variáveis de ambiente ou fazer com que o processo de compilação as solicite.

Para receber essas senhas de variáveis de ambiente:

Groovy

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

Kotlin

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

Para que o processo de compilação solicite essas senhas se invocar o build a partir da linha de comando:

Groovy

storePassword System.console().readLine("\nKeystore password: ")
keyPassword System.console().readLine("\nKey password: ")

Kotlin

storePassword = System.console().readLine("\nKeystore password: ")
keyPassword = System.console().readLine("\nKey password: ")

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 protegido e confirme se os respectivos backups estão protegidos. 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

Na publicação de apps do Wear OS, o APK do relógio e o APK opcional do smartphone precisam ser assinados. Para ver mais informações sobre o empacotamento e a assinatura de apps do Wear OS, consulte Empacotar e distribuir apps do Wear.