Cómo crear varios APK

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

Gradle puede crear APKs independientes que solo contengan código y recursos específicos para cada ABI. En esta página, se describe cómo configurar tu compilación para generar varios APK. Si necesitas crear diferentes versiones de tu app que no se basen en la ABI, usa variantes de compilación.

Cómo configurar tu compilación para varios APK

Para configurar tu compilación para varios APKs, agrega un bloque splits a tu archivo build.gradle a nivel del módulo. Dentro del bloque splits, proporciona un bloque abi que especifique cómo quieres que Gradle genere APK por ABI.

Cómo configurar varios APK para las ABI

Para crear APKs independientes para diferentes ABIs, agrega un bloque abi dentro de tu bloque splits. En tu bloque abi, proporciona una lista de las ABI que elegiste.

Para configurar varios APK por ABI, se usan las siguientes opciones de Gradle DSL:

enable para Groovy o isEnable para la secuencia de comandos de Kotlin
Si configuras este elemento como true, Gradle genera varios APKs según las ABIs que definas. El valor predeterminado es false.
exclude
Especifica una lista de ABI separadas por comas para las que no quieres que Gradle genere APKs independientes. Usa exclude si quieres generar APKs para la mayoría de las ABI, pero necesitas excluir algunas que tu app no admite.
reset()

Borra la lista predeterminada de ABIs. Solo se usa cuando se combina con el elemento include para especificar las ABI que deseas agregar.

En el siguiente fragmento, se establece la lista de ABI solo 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 una lista separada por comas de las ABI para las que quieres que Gradle genere APKs. Solo se debe usar junto con reset() para especificar una lista exacta de ABIs.

universalApk para Groovy o isUniversalApk para la secuencia de comandos de Kotlin

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

En el siguiente ejemplo, se genera un APK independiente para cada ABI: x86 y x86_64. Para ello, se usa reset() para comenzar con una lista vacía de ABI, seguida de include con una lista de ABI que obtienen un APK cada una.

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

Para obtener una lista de las ABIs compatibles, consulta ABIs compatibles.

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 tiene dos columnas para los proyectos sin código nativo/C++.
Figura 1: El panel Build Variants especifica dos columnas para los proyectos que no tienen código nativo/C++.

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

Proyectos con código nativo/C++

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

El panel Build Variants agrega la columna Active ABI para los proyectos con código nativo/C++.
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 que 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 la ABI, sigue estos pasos:

  1. Haz clic en la celda de la columna variante de compilación activa o ABI activa.
  2. Selecciona la variante o ABI que elegiste 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 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 para la misma app que tengan la misma información de versión, debes asegurarte de que cada APK tenga un versionCode único antes de subirlos a Play Store.

Puedes configurar el archivo build.gradle de nivel de módulo para anular el elemento versionCode de cada APK. Si creas una asignación que asigne un valor numérico único para cada ABI para la que configures varios APK, puedes anular el código de versión de salida con un valor que combine el código de versión definido dentro del bloque defaultConfig o productFlavors con el valor numérico asignado a la ABI.

En el siguiente ejemplo, el APK para la ABI x86 obtiene un versionCode de 2004 y la ABI x86_64 obtiene un versionCode de 3004.

Asignar códigos de versión en incrementos grandes, 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 de x86 y 3005 al APK de x86_64.

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]

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)

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 ver 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 configures tu archivo build.gradle o build.gradle.kts a nivel del módulo para compilar varios APKs, haz clic en Build > Build APK para compilar todos los APKs del módulo seleccionado en el panel Project. Gradle creará los APK para cada ABI en el directorio build/outputs/apk/ del proyecto.

Gradle compila un APK para cada ABI para la que configures varios APKs.

Por ejemplo, el siguiente fragmento de build.gradle habilita la compilación de varios APK para las ABI x86 y x86_64:

Groovy

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

Kotlin

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

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

  • app-X86-release.apk: Contiene código y recursos para la ABI x86.
  • app-X86_64-release.apk: Contiene código y recursos para la ABI x86_64.

Cuando se compilan varios APK basados en ABI, Gradle solo genera un APK que incluye código y 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 del nombre de archivo APK

Cuando se compilan varios APKs, Gradle genera nombres de archivo de APK con el siguiente esquema:

modulename-ABI-buildvariant.apk

Los componentes del esquema son los siguientes:

modulename
Especifica el nombre del módulo que se está compilando.
ABI
Si se habilitan varios APK para ABI, se especifica la ABI para el APK, como x86.
buildvariant
Especifica la variante de compilación que se está compilando, como debug.