Criar vários APKs

Se você publicar seu app no Google Play, será necessário criar e fazer upload de um Android App Bundle. Quando você faz isso, o Google Play gera e exibe automaticamente APKs otimizados para a configuração do dispositivo de cada usuário para que eles façam o download apenas do código e dos recursos necessários para executar o app. A publicação de vários APKs é útil se você está publicando em um armazenamento que não ofereça suporte ao formato AAB. Nesse caso, você precisa criar, assinar e gerenciar cada APK por conta própria.

Embora seja recomendável criar um único APK para oferecer compatibilidade com todos os dispositivos de destino sempre que possível, isso pode resultar em um APK muito grande devido aos arquivos necessários para que haja compatibilidade com várias densidades de tela ou Interfaces binárias do aplicativo (ABIs, na sigla em inglês). Uma maneira de reduzir o tamanho do APK é criar vários APKs que contenham arquivos para densidades de tela ou ABIs específicas.

O Gradle pode criar APKs separados que contenham apenas códigos e recursos específicos para cada densidade ou ABI. Esta página descreve como configurar seu build para gerar vários APKs. Caso precise criar versões diferentes do app que não sejam baseadas na densidade de tela ou na ABI, você pode usar variantes de build.

Configurar a build para vários APKs

Para configurar o build para vários APKs, adicione um bloco splits ao arquivo de nível de módulo build.gradle (link em inglês). No bloco splits, forneça um bloco density que especifique como o Gradle precisa gerar APKs por densidade ou um bloco abi que especifique como ele precisa gerar APKs por ABI. Você pode fornecer blocos de densidade e ABI, e o sistema de build cria um APK para cada combinação de densidade e ABI.

Configurar vários APKs para densidades de tela

Para criar APKs separados para diferentes densidades de tela, adicione um bloco density dentro do bloco splits. No bloco density, forneça uma lista de densidades de tela e dos tamanhos de tela compatíveis. A lista de tamanhos de tela compatíveis só será usada se você precisar de elementos <compatible-screens> específicos no manifesto de cada APK.

As seguintes opções de DSL do Gradle são usadas para configurar vários APKs para densidades de tela:

enable
Se você definir esse elemento como true, o Gradle gerará vários APKs com base nas densidades de tela definidas. O valor padrão é false.
exclude
Especifica uma lista de densidades separadas por vírgulas para as quais o Gradle não gerará APKs separados. Caso queira gerar APKs para a maioria das densidades, mas precise excluir algumas que não oferecem suporte ao app, use exclude.
reset()
Apaga a lista padrão de densidades de tela. Use somente quando combinado com o elemento include para especificar as densidades que você quer adicionar. O snippet a seguir define a lista de densidades como ldpi e xxhdpi chamando reset() para limpar a lista e, em seguida, usando include.
reset()  // Clears the default list from all densities to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities we want to generate APKs for.
include
Especifica uma lista de densidades separadas por vírgulas para as quais o Gradle gerará APKs. Use somente em combinação com reset() para especificar uma lista exata de densidades.
compatibleScreens
Especifica uma lista separada por vírgulas de tamanhos de tela compatíveis. Isso injetará um nó <compatible-screens> correspondente no manifesto para cada APK. Essa configuração é uma maneira conveniente de gerenciar as densidades e os tamanhos de tela na mesma seção build.gradle. No entanto, usar <compatible-screens> pode limitar os tipos de dispositivos em que seu app funcionará. Para conhecer outras formas de suporte a diferentes tamanhos de tela, consulte Suporte a várias telas.

Como cada APK que é baseado na densidade de tela inclui uma tag <compatible-screens> com restrições específicas sobre os tipos de tela com suporte, mesmo que você publique vários, alguns dispositivos novos não vão corresponder aos diversos filtros de APK. Assim, o Gradle sempre gera um outro APK universal contendo recursos para todas as densidades de tela e não inclui uma tag <compatible-screens>. É necessário publicar esse APK universal com seus APKs por densidade para fornecer um substituto para dispositivos que não correspondam aos APKs com uma tag <compatible-screens>.

O exemplo a seguir gera um APK separado para cada densidade de tela listada em Conjunto de telas compatíveis, exceto ldpi, xxhdpi e xxxhdpi. Isso é feito usando exclude para remover três densidades da lista padrão de todas as densidades.

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on screen density.
    density {

      // Configures multiple APKs based on screen density.
      enable true

      // Specifies a list of screen densities Gradle should not create multiple APKs for.
      exclude "ldpi", "xxhdpi", "xxxhdpi"

      // Specifies a list of compatible screen size settings for the manifest.
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
}

Kotlin

android {
    ...
    splits {

        // Configures multiple APKs based on screen density.
        density {

            // Configures multiple APKs based on screen density.
            isEnable = true

            // Specifies a list of screen densities Gradle should not create multiple APKs for.
            exclude("ldpi", "xxhdpi", "xxxhdpi")

            // Specifies a list of compatible screen size settings for the manifest.
            compatibleScreens("small", "normal", "large", "xlarge")
        }
    }
}

Para acessar uma lista de nomes de densidade e de tamanhos de tela, consulte Como oferecer suporte a várias telas. Para ver mais detalhes sobre como distribuir seu app para tipos de tela e dispositivos específicos, consulte Distribuição para telas específicas.

Configurar vários APKs para ABIs

Para criar APKs separados para diferentes ABIs, adicione um bloco abi dentro do seu bloco splits. No bloco abi, forneça uma lista de ABIs desejadas.

As seguintes opções de DSL do Gradle são usadas para configurar vários APKs por ABI:

enable
Se esse elemento for definido como true, o Gradle gerará vários APKs com base nas ABIs definidas. O valor padrão é false
exclude
Especifica uma lista de ABIs separadas por vírgulas para as quais o Gradle não gerará APKs separados. Use exclude caso queira gerar APKs para a maioria das ABIs, mas precise excluir algumas que não oferecem suporte ao seu app.
reset()
Limpa a lista padrão de ABIs. Use somente quando combinado com o elemento include para especificar a ABI que você quer adicionar. O snippet a seguir define a lista de ABIs como x86 e x86_64, chamando reset() para limpar a lista e, em seguida, usando include:
reset()  // Clears the default list from all ABIs to no ABIs.
include "x86", "x86_64" // Specifies the two ABIs we want to generate APKs for.
include
Especifica uma lista de ABIs separadas por vírgulas para as quais o Gradle gerará APKs. Use somente em combinação com reset() para especificar uma lista exata de ABIs.
universalApk
Se for true, o Gradle gerará um APK universal, além dos APKs por ABI. Um APK universal contém código e recursos para todas as ABIs em um único APK. O valor padrão é false. Observe que essa opção só está disponível no bloco splits.abi. Ao criar vários APKs com base na densidade da tela, o Gradle sempre gera um APK universal que contém código e recursos para todas as densidades.

O exemplo a seguir gera um APK separado para cada ABI: x86 e x86_64. Isso é feito usando reset() para começar com uma lista vazia de ABIs, seguida por include com uma lista das ABIs que receberão um APK.

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      enable true

      // By default all ABIs are included, so use reset() and include to specify that we only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs that Gradle should create APKs for to none.
      reset()

      // Specifies a list of ABIs that Gradle should create APKs for.
      include "x86", "x86_64"

      // Specifies that we do not want to also generate a universal APK that includes all ABIs.
      universalApk false
    }
  }
}

Kotlin

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      isEnable = true

      // By default all ABIs are included, so use reset() and include to specify that we only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs that Gradle should create APKs for to none.
      reset()

      // Specifies a list of ABIs that Gradle should create APKs for.
      include("x86", "x86_64")

      // Specifies that we do not want to also generate a universal APK that includes all ABIs.
      isUniversalApk = false
    }
  }
}

Consulte ABIs compatíveis para ver uma lista daquelas que são compatíveis.

mips, mips64 e armeabi

Por padrão, o plug-in do Android para Gradle 3.1.0 e versões mais recentes não gera mais APKs para as ABIs mips, mips64 e armeabi. Isso porque o NDK r17 e versões mais recentes não incluem mais essas ABIs como destinos com suporte.

Primeiro, verifique o Google Play Console para confirmar se há usuários fazendo o download de APKs do seu app voltados a essas ABIs. Se não for o caso, recomendamos que eles sejam omitidos do build. Caso queira continuar criando APKs voltados para essas ABIs, use o NDK r16b ou versões anteriores, defina as variantes de compilação e as ABIs ativas e especifique as ABIs no seu arquivo build.gradle, conforme mostrado abaixo.

Groovy

splits {
    abi {
        include 'armeabi', 'mips', 'mips64'
        ...
    }
}

Kotlin

splits {
    abi {
        include ("armeabi", "mips", "mips64")
        ...
    }
}

Problema conhecido: caso esteja usando o plug-in do Android para Gradle 3.0.1 ou versão anterior com NDK r17 ou mais recente, você pode receber o erro Error:ABIs [mips64, armeabi, mips] are not supported for platform.. Isso ocorre porque versões mais antigas do plug-in ainda incluem ABIs sem suporte por padrão ao criar APKs individuais. Para resolver esse problema, atualize o plug-in para a versão mais recente ou, no arquivo build.gradle do seu app, redefina a lista padrão de ABIs do plug-in e inclua apenas as ABIs com suporte, conforme mostrado abaixo.

Groovy

...
splits {
    abi {
        ...
        reset()
        include "x86", "armeabi-v7a", "arm64-v8a", "x86_64"
    }
}

Kotlin

...
splits {
    abi {
        ...
        reset()
        include("x86", "armeabi-v7a", "arm64-v8a", "x86_64")
    }
}

Projetos sem código nativo/C++

O painel Build Variants tem duas colunas: Module e Active Build Variant. O valor de Active Build Variant para o módulo determina a variante de compilação que será implantada e ficará visível no editor.

Figura 1: o painel Build Variants tem duas colunas para projetos que não têm código nativo/C++.

Para alternar entre variantes, clique na célula Active Build Variant de um módulo e escolha a variante desejada no campo de lista.

Projetos com código nativo/C++

O painel Build Variants tem três colunas: Module, Active Build Variant e Active ABI. O valor de Active Build Variant para o módulo determina a variante de compilação que será implantada e ficará visível no editor. Para módulos nativos, o valor de Active ABI determina a ABI que o editor usa, mas não afeta o que é implantado.

Figura 2: o painel Build Variants adiciona a coluna Active ABI para projetos com código nativo/C++.

Para mudar o tipo de build ou a ABI, clique na célula da coluna Active Build Variant ou Active ABI e escolha a variante ou a ABI desejada no campo da lista. Uma nova sincronização é executada automaticamente. Mudar a coluna para um módulo de app ou de biblioteca aplicará a modificação em todas as linhas dependentes.

Configurar o controle de versões

Por padrão, quando o Gradle gera vários APKs, cada um deles tem as mesmas informações de versão, conforme especificado no arquivo build.gradle do módulo. Como a Google Play Store não permite que o mesmo app tenha vários APKs com as mesmas informações de versão, é necessário garantir que cada APK tenha o próprio versionCode exclusivo antes de fazer upload para a Play Store.

Você pode configurar o arquivo build.gradle do módulo para substituir o versionCode de cada APK. Ao criar um mapeamento que atribua um valor numérico exclusivo para cada ABI e densidade para as quais vários APKs são configurados, você pode substituir o código da versão de saída por um valor que combine o código de versão definido no bloco defaultConfig ou productFlavors com o valor numérico atribuído à densidade ou ABI.

No exemplo a seguir, o APK da ABI x86 receberia um versionCode de 2004 e a ABI x86_64 receberia 3004. Atribuir códigos de versão em incrementos maiores, como 1000, permite que posteriormente códigos de versão exclusivos sejam atribuídos caso seja necessário atualizar seu app. Por exemplo, se defaultConfig.versionCode itera para 5 em uma atualização subsequente, o Gradle atribui um versionCode de 2005 ao APK x86 e 3005 ao APK x86_64.

Dica: se seu build inclui um APK universal, atribua um valor de versionCode menor que o dos outros APKs. Como a Google Play Store instala a versão do app compatível com o dispositivo de destino e que tem o maior versionCode, atribuir um versionCode menor ao APK universal garante que a Play Store tente instalar um dos seus APKs antes do APK universal. O exemplo de código abaixo processa isso sem substituir o versionCode padrão de um APK universal.

Groovy

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, x86:2, x86_64:3]

// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code does not override the version code for universal APKs.
    // However, because we want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the version code
      // for only the output APK, not for the variant itself. Skipping this step simply
      // causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "x86" to 2, "x86_64" to 3)

// For per-density APKs, create a similar map like this:
// val densityCodes = mapOf("mdpi" to 1, "hdpi" to 2, "xhdpi" to 3)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code does not override the version code for universal APKs.
            // However, because we want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

Para ver mais exemplos de esquemas de código de versão alternativos, consulte Como atribuir códigos de versão.

Criar vários APKs

Depois de configurar o arquivo build.gradle do módulo para criar vários APKs, clique em Build > Build APK para criar todos os APKs para o módulo selecionado no painel Project. O Gradle cria os APKs para cada densidade ou ABI no diretório build/outputs/apk/ do projeto.

O Gradle cria um APK para cada densidade ou ABI para as quais você configura vários APKs. Se você habilitar vários APKs para densidades e ABIs, o Gradle criará um APK para cada combinação desses elementos. Por exemplo, o snippet build.gradle a seguir permite criar vários APKs para densidades mdpi e hdpi, bem como para ABIs x86 e x86_64.

Groovy

...
  splits {
    density {
      enable true
      reset()
      include "mdpi", "hdpi"
    }
    abi {
      enable true
      reset()
      include "x86", "x86_64"
    }
  }

Kotlin

...
  splits {
    density {
      enable true
      reset()
      include "mdpi", "hdpi"
    }
    abi {
      enable true
      reset()
      include "x86", "x86_64"
    }
  }

O resultado da configuração de exemplo inclui os quatro APKs a seguir:

  • app-hdpiX86-release.apk: contém somente o código e os recursos da densidade hdpi e da ABI x86.
  • app-hdpiX86_64-release.apk: contém somente o código e os recursos da densidade hdpi e da ABI x86_64.
  • app-mdpiX86-release.apk: contém somente o código e os recursos da densidade mdpi e da ABI x86.
  • app-mdpiX86_64-release.apk: contém somente o código e os recursos da densidade mdpi e da ABI x86_64.

Ao criar vários APKs com base na densidade de tela, o Gradle sempre gera um APK universal que inclui código e recursos para todas as densidades de tela, além dos APKs por densidade. Ao criar vários APKs com base na ABI, o Gradle só gera um APK que inclui código e recursos para todas as ABIs se você especificar universalApk true no bloco splits.abi do seu arquivo build.gradle.

Formato de nome de arquivo do APK

Ao criar vários APKs, o Gradle usa nomes de arquivos do APK com o seguinte esquema:

modulename-screendensityABI-buildvariant.apk

Os componentes do esquema são:

modulename
Especifica o nome do módulo que está sendo criado.
screendensity
Se vários APKs para densidade da tela estão habilitados, especifica a densidade do APK, como "mdpi".
ABI
Se vários APKs para ABI estão ativados, especifica a ABI do APK, como "x86". Se vários APKs para densidade de tela e ABI estão ativados, o Gradle concatena o nome da densidade com o nome da ABI, por exemplo, "mdpiX86". Se universalApk estiver ativado para APKs por ABI, o Gradle usará "universal" como a parte ABI do nome de arquivo do APK universal.
buildvariant
Especifica a variante de build que está sendo criada, por exemplo, "debug".

Por exemplo, ao criar o APK de densidade da tela mdpi para a versão de depuração de "myApp", o nome do arquivo do APK será myApp-mdpi-debug.apk. A versão de lançamento de "myApp", configurada para criar vários APKs para a densidade da tela mdpi e a ABI x86, terá myApp-mdpiX86-release.apk como o nome de arquivo do APK.