Cómo crear varios APK

Precaución: A partir de agosto de 2021, todas las apps nuevas deben publicarse como paquetes de aplicación. Para publicar tu app en Google Play, compila y sube un Android App Bundle. Cuando lo haces, Google Play genera y publica automáticamente APK optimizados para la configuración del dispositivo de cada usuario, por lo que este solo descarga el código y los recursos que necesita para ejecutar tu app. Publicar varios APK es útil si publicas en una tienda que no admite el formato AAB. En ese caso, deberás compilar, firmar y administrar cada APK tú mismo.

Si bien es mejor compilar un solo APK que admita todos los dispositivos de destino siempre que sea posible, quizás se genere uno muy grande debido a que los archivos admiten varias densidades de pantalla o interfaces binarias de la aplicación (ABIs). Una forma de reducir el tamaño de tu APK es crear varios APK que contengan archivos para ABI o densidades de pantalla específicas.

Gradle puede crear APK separados que solo contengan el código y los recursos específicos de cada densidad o ABI. En esta página, se describe cómo configurar tu compilación a fin de generar varios APK. Si necesitas crear diferentes versiones de tu app que no se basen en la densidad de pantalla ni ABI, usa variantes de compilación en su lugar.

Cómo configurar tu compilación para varios APK

A fin de configurar tu compilación para varios APK, agrega un bloque splits a tu archivo build.gradle a nivel del módulo. Dentro del bloque splits, incluye un bloque density que especifique la forma en que quieres que Gradle genere los APK por densidad, o bien un bloque abi que especifique la manera en que quieres que Gradle genere los APK por ABI. Puedes proporcionar bloques de ABI y densidad, y el sistema de compilación creará un APK para cada combinación de ABI y densidad.

Cómo configurar varios APK para densidades de pantalla

A fin de crear APK independientes para diferentes densidades de pantalla, agrega un bloque density en tu bloque splits. En el bloque density, proporciona una lista de las densidades de pantalla deseadas y los tamaños de pantalla compatibles. Usa la lista de tamaños de pantalla compatibles solo si necesitas elementos <compatible-screens> específicos en el manifiesto de cada APK.

Las siguientes opciones de Gradle DSL se usan a fin de configurar varios APK para densidades de pantalla:

enable para Groovy, isEnable para la secuencia de comandos de Kotlin
Si configuras este elemento en true, Gradle generará varios APK basados en las densidades de pantalla que definas. El valor predeterminado es false.
exclude
Especifica la lista de densidades, separadas por comas, para las que no quieres que Gradle genere APK separados. Usa exclude si quieres generar APK para la mayoría de las densidades, pero necesitas excluir aquellas que tu app no admite.
reset()

Borra la lista predeterminada de densidades de pantalla. Solo debe usarse junto con el elemento include a fin de especificar las densidades que quieras agregar.

En el siguiente fragmento, se configura la lista de densidades únicamente en ldpi y xxhdpi llamando a reset() para borrar la lista y, luego, usando include:

reset()                  // Clears the default list from all densities
                         // to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities to generate APKs
                         // for.
include
Especifica la lista de densidades, separadas por comas, para las que quieres que Gradle genere los APKs. Solo se usa junto con reset() a fin de especificar la lista exacta de densidades.
compatibleScreens

Especifica la lista de tamaños de pantalla compatibles separados por comas. De esta manera, se inserta un nodo <compatible-screens> que coincide en el manifiesto para cada APK.

Esta opción permite administrar de manera conveniente las densidades y los tamaños de pantalla en la misma sección de build.gradle. Sin embargo, usar <compatible-screens> puede limitar los tipos de dispositivos con los que funciona tu app. Para ver maneras alternativas de admitir diferentes tamaños de pantalla, consulta la descripción general de compatibilidad de pantalla.

Debido a que cada APK que se basa en una densidad de pantalla incluye una etiqueta <compatible-screens> con restricciones específicas sobre los tipos de pantalla que admite el APK (incluso si publicas varios APK), algunos dispositivos nuevos no coinciden con tus filtros de varios APK. Por lo tanto, Gradle siempre genera un APK universal adicional que contiene recursos para todas las densidades de pantalla y no incluye una etiqueta <compatible-screens>. Publica este APK universal junto con los APK por densidad para proporcionar un resguardo para los dispositivos que no coincidan con los APK que incluyen una etiqueta <compatible-screens>.

En el siguiente ejemplo, se genera un APK separado para cada densidad de pantalla, excepto ldpi, xxhdpi y xxxhdpi. Para ello, se usa exclude a fin de quitar esas tres densidades de la lista predeterminada de todas las 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 you don't want Gradle to 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 you don't want Gradle to 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 ver la lista de los nombres de densidades y tamaños de pantalla, consulta Cómo brindar compatibilidad con diferentes tamaños de pantalla. Si deseas obtener más información para personalizar diferentes variantes de compilación de tu app para tipos de pantalla y dispositivos específicos, consulta Cómo declarar una compatibilidad de pantalla restringida.

Cómo configurar varios APK para las ABI

A fin de crear APK separados para diferentes ABI, agrega un bloque abi dentro de tu bloque splits. En el bloque abi, proporciona la lista de las ABI deseadas.

Las siguientes opciones de Gradle DSL se usan para configurar varios APK por ABI:

enable para Groovy o isEnable para la secuencia de comandos de Kotlin
Si configuras este elemento en true, Gradle generará varios APK basados en las ABI que definas. El valor predeterminado es false.
exclude
Especifica la lista de las ABI, separadas por comas, para las que no quieres que Gradle genere APK separados. Usa exclude si quieres generar APK para la mayoría de las ABI, pero necesitas excluir aquellas que tu app no admite.
reset()

Borra la lista predeterminada de ABI. Solo debe usarse junto con el elemento include para especificar las ABI que deseas agregar.

En el siguiente fragmento, se configura la lista de ABI únicamente en x86 y x86_64 llamando a reset() para borrar la lista y, luego, 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 la lista de las ABI, separadas por comas, para las que quieres que Gradle genere los APK. Solo se usa junto con reset() para especificar la lista exacta de ABI.
universalApk para Groovy o isUniversalApk para la secuencia de comandos de Kotlin

Si es true, Gradle generará un APK universal además de los APK por ABI. Un APK universal contiene el código y los recursos para todas las ABI en un solo APK. El valor predeterminado es false.

Ten en cuenta que esta opción solo está disponible en el bloque splits.abi. Al compilar varios APK basados en densidades de pantalla, Gradle siempre generará un APK universal con el código y los recursos para todas las densidades de pantalla.

En el siguiente ejemplo, se genera un APK separado para cada ABI: x86 y x86_64. Para ello, se usa reset() a fin de comenzar con la lista vacía de ABI, seguida de include con la lista de las ABI para las que se obtiene un 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 you only
      // want APKs for x86 and x86_64.

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

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

      // Specifies that you don't 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 you only
      // want APKs for x86 and x86_64.

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

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

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      isUniversalApk = false
    }
  }
}

Consulta la lista de las ABI admitidas aquí.

Proyectos sin código nativo/C++

Para los proyectos sin código nativo/C++, el panel Build Variants tiene dos columnas: Module y Active Build Variant, como se muestra en la figura 1.

El panel Variantes de compilación
Figura 1: El panel Build Variants especifica dos columnas para los proyectos sin código nativo/C++.

El valor Active Build Variant del módulo determina la variante de compilación que se implementa y es visible en el editor. Para alternar entre variantes, haz clic en la celda Active Build Variant de un módulo y elige la variante que desees en el campo de lista.

Proyectos con código nativo/C++

Para los proyectos con código nativo/C++, el panel Build Variants tiene tres columnas: Module, Active Build Variant y Active ABI, como se muestra en la figura 2.

Figura 2: El panel Build Variants agrega la columna Active ABI para los proyectos con código nativo/C++.

El valor Active Build Variant del módulo determina la variante de compilación que se implementa y se puede ver en el editor. En el caso de los módulos nativos, el valor Active ABI determina la ABI que usa el editor, pero no afecta lo que se implementa.

Para cambiar el tipo de compilación o ABI, haz lo siguiente:

  1. Haz clic en la celda de la columna Active Build Variant o Active ABI.
  2. Elige la variante o la ABI que desees en el campo de la lista. Se ejecuta automáticamente una nueva sincronización.

Si modificas cualquiera de las columnas de una app o un módulo de biblioteca, se aplicará el cambio a todas las filas dependientes.

Cómo configurar el control de versiones

De forma predeterminada, cuando Gradle genera varios APK, cada uno de ellos tiene la misma información de versión, como se especifica en el archivo build.gradle o build.gradle.kts a nivel del módulo. Como Google Play Store no permite varios APK con la misma información de versión para la misma app, deberás asegurarte de que cada APK tenga un versionCode único antes de subirlo a Play Store.

Puedes configurar el archivo build.gradle a nivel del módulo para que anule el versionCode de cada APK. Si creas una asignación que asigna un valor numérico único a cada ABI y densidad para las que configuras varios APK, puedes anular el código de versión de salida con un valor que combina el código de versión definido en el bloque defaultConfig o productFlavors con el valor numérico asignado a la densidad o ABI.

En el siguiente ejemplo, el APK de la ABI x86 obtiene un versionCode de 2,004, y la ABI x86_64 obtiene un versionCode de 3,004.

Asignar códigos de versión en grandes incrementos, como 1,000, te permite asignar códigos de versión únicos más adelante si necesitas actualizar tu app. Por ejemplo, si defaultConfig.versionCode se repite hasta 5 en una actualización posterior, Gradle asigna un versionCode de 2005 al APK x86 y un 3005 al de x86_64.

Sugerencia: Si tu compilación incluye un APK universal, asígnale un versionCode que sea inferior al de los otros APK. Dado que Google Play Store instala la versión de tu app que es compatible con el dispositivo objetivo y tiene el valor versionCode más alto, asignar un versionCode inferior al del APK universal garantiza que Google Play Store intente instalar uno de tus APK antes de recurrir al APK universal. En el siguiente código de ejemplo, se realiza esta acción sin anular el versionCode predeterminado de un 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:
// 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 doesn't override the version code for universal APKs.
    // However, because you 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 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:
// 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 doesn't override the version code for universal APKs.
            // However, because you 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 obtener más ejemplos de esquemas de códigos de versión alternativos, consulta Cómo asignar códigos de versión.

Cómo crear varios APK

Una vez que hayas configurado el archivo build.gradle o build.gradle.kts a nivel del módulo para compilar varios APK, haz clic en Build > Build APK para compilar todos los APKs del módulo seleccionado actualmente en el panel Project. Gradle crea los APK para cada densidad o ABI en el directorio build/outputs/apk/ del proyecto.

Gradle compila un APK de cada densidad o ABI para las que configuras varios APK. Si habilitas varios APK tanto para las densidades como para las ABI, Gradle creará un APK para cada combinación de densidad y ABI.

Por ejemplo, en el siguiente fragmento de build.gradle, se habilita la compilación de varios APK para las densidades de mdpi y hdpi, y también para las ABI x86 y x86_64:

Groovy

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

Kotlin

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

En el resultado de la configuración de ejemplo, se incluyen los 4 APK siguientes:

  • app-hdpiX86-release.apk: Contiene código y recursos para la densidad hdpi y la ABI x86.
  • app-hdpiX86_64-release.apk: Contiene código y recursos para la densidad hdpi y la ABI x86_64.
  • app-mdpiX86-release.apk: Contiene código y recursos para la densidad mdpi y la ABI x86.
  • app-mdpiX86_64-release.apk: Contiene código y recursos para la densidad mdpi y la ABI x86_64.

Al compilar varios APK basados en densidades de pantalla, Gradle siempre genera un APK universal que incluye el código y los recursos para todas las densidades, además de los APK por densidad.

Cuando compilas varios APK basados en la ABI, Gradle solo genera un APK que incluye el código y los recursos para todas las ABI si especificas universalApk true en el bloque splits.abi de tu archivo build.gradle (para Groovy) o isUniversalApk = true en el bloque splits.abi de tu archivo build.gradle.kts (para la secuencia de comandos de Kotlin).

Formato de nombre de archivo APK

Cuando compilas varios APK, Gradle genera nombres de archivos APK con el siguiente esquema:

modulename-screendensityABI-buildvariant.apk

Los componentes del esquema son los siguientes:

modulename
Especifica el nombre del módulo que se está compilando.
screendensity
Si se habilitan varios APK para densidades de pantalla, especifica la densidad de pantalla del APK, por ejemplo, mdpi.
ABI

Si se habilitan varios APK para la ABI, especifica la ABI del APK, por ejemplo, x86.

Si se habilitan varios APK para la densidad de pantalla y la ABI, Gradle concatena el nombre de la densidad con el nombre de la ABI, por ejemplo, mdpiX86. Si universalApk está habilitado para los APK por ABI, Gradle usa universal como la parte de la ABI del nombre de archivo del APK universal.

buildvariant
Especifica la variante de compilación que se está compilando, como debug.

Por ejemplo, al compilar un APK de densidad de pantalla mdpi para la versión de depuración de myApp, el nombre de archivo del APK es myApp-mdpi-debug.apk. La versión de lanzamiento de myApp que se configuró para compilar varios APK tanto para la densidad de pantalla mdpi como para la ABI x86 tiene el nombre de archivo del APK de myApp-mdpiX86-release.apk.