Configurar variantes de compilação

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 dentro do 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 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 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.

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:

  1. Na barra da janela de ferramentas, clique em Gradle.
  2. 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:

    1. Clique em File > Settings > Experimental (no macOS, Android Studio > Settings > Experimental).
    2. Desmarque a opção Do not build Gradle task list during Gradle sync.
  3. 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":

  1. Abra o painel Project e selecione a visualização Project no menu localizado na parte de cima do painel.
  2. Navegue para MyProject/app/src/.
  3. Clique com o botão direito do mouse no diretório src e selecione New > Directory.
  4. No menu em Gradle Source Sets, selecione full/java.
  5. 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":

  1. No 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 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 é semelhante à Figura 1.

Figura 1. Novos diretórios de conjuntos de origem para o tipo de build "debug".

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:

  1. src/demoDebug/ (conjunto de origem da variante de build)
  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)

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/ ou java/ é 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 e src/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 do Utility.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 origem main/.

  • 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/ e asset/ 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 bloco defaultConfig 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 bloco productFlavors, 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:

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