Cómo configurar variantes de compilación

En esta página, se muestra la manera de configurar variantes de compilación para crear diferentes versiones de tu app a partir de un mismo proyecto, y de administrar correctamente tus dependencias y configuraciones de firma.

Cada variante de compilación representa una versión diferente de tu app que puedes compilar. Por ejemplo, es posible que desees compilar una versión de tu app que sea gratuita con contenido limitado y una versión pagada que incluya más contenido. También puedes compilar diferentes versiones de tu app para diferentes dispositivos, según el nivel de API y otras variantes de dispositivos.

Las variantes de compilación surgen del uso de un conjunto específico de reglas a través de Gradle para combinar los parámetros de configuración, código y recursos configurados en tus tipos de compilación y variantes de productos. Aunque no configuras variantes de compilación directamente, sí configuras los tipos de compilación y las variantes de productos que las forman.

Por ejemplo, en una variante de producto "demo", pueden especificarse determinadas características y requisitos de dispositivo, como código fuente personalizado, y recursos y niveles de API mínimos, mientras que en el tipo de compilación "debug" se aplican diferentes parámetros de configuración de compilación y empaquetado, como opciones de depuración y claves de firma. La variante de compilación que combina ambos es la versión "demoDebug" de tu app, que contiene una combinación de configuraciones y recursos incluidos en la variante de producto "demo", el tipo de compilación "debug" y conjunto de orígenes main/.

Cómo configurar tipos de compilaciones

Puedes crear y configurar tipos de compilaciones dentro del bloque android del archivo build.gradle.kts del nivel del módulo. Cuando creas un módulo nuevo, Android Studio crea automáticamente los tipos de compilación de depuración y lanzamiento. Si bien el tipo de compilación de depuración no aparece en el archivo de configuración de compilación, Android Studio lo configura con debuggable true. Esta configuración te permite depurar la app en dispositivos Android seguros y configura la firma de la app con un almacén de claves de depuración genérico.

Puedes agregar el tipo de compilación de depuración a tu configuración si deseas agregar o cambiar determinadas opciones de configuración. En el siguiente ejemplo, se especifica un applicationIdSuffix para el tipo de compilación de depuración y se configura un tipo de compilación "staging" (etapa de pruebas) que se inicializa con la configuración del tipo de compilación de depuración:

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

Nota: Cuando realizas cambios en un archivo de configuración de la compilación, Android Studio exige que sincronices tu proyecto con la nueva configuración. Para hacerlo, haz clic en Sync Now en la barra de notificaciones que aparece cuando realizas un cambio, o bien en Sync Project en la barra de herramientas. Si Android Studio detecta un error en la configuración, aparecerá la ventana Messages con la descripción del problema.

Para obtener más información sobre todas las propiedades que puedes configurar con tipos de compilación, lee la referencia de BuildType.

Cómo configurar variantes de productos

Crear tipos de productos es similar a crear tipos de compilación. Agrega variantes de productos al bloque productFlavors de tu configuración de compilación e incluye los parámetros de configuración que desees. Las variantes de productos admiten las mismas propiedades que defaultConfig, ya que defaultConfig en realidad pertenece a la clase ProductFlavor. Eso significa que puedes proporcionar la configuración básica para todas las variantes en el bloque defaultConfig, y cada una puede cambiar cualquiera de estos valores predeterminados; por ejemplo, applicationId. Para obtener más información sobre el ID de aplicación, consulta Cómo establecer el ID de aplicación.

Nota: No obstante, debes especificar un nombre de paquete usando el atributo package en el archivo de manifiesto main/. También debes usar ese nombre de paquete en tu código fuente para hacer referencia a la clase R, o bien resolver cualquier actividad o registro de servicio relacionados. Esto te permite usar applicationId para asignar a cada variante de producto un ID exclusivo para el empaquetado y la distribución, sin necesidad de cambiar el código fuente.

Todas las variantes deben pertenecer a una dimensión denominada, que es un grupo de variantes de productos. Debes asignar todas las variantes a una dimensión; de lo contrario, obtendrás el siguiente error de compilación.

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

Si un módulo dado especifica solo una dimensión de variantes, el complemento de Android para Gradle asignará automáticamente todas las variantes del módulo a esa dimensión.

En la siguiente muestra de código, se crea una dimensión de variantes denominada "version" y se agregan las variantes de producto "demo" y "full". Estas variantes brindan sus propios applicationIdSuffix y 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"
        }
    }
}

Nota: Si tienes una app heredada que distribuyes con APKs en Google Play (que se creó antes de agosto de 2021) y deseas distribuirla con compatibilidad con varios APK en Google Play, asigna el mismo valor de applicationId a todas las variantes y otorga a cada variante un versionCode diferente. Para distribuir diferentes variantes de tu app como apps independientes en Google Play, debes asignar un applicationId diferente a cada variante.

Después de crear y configurar tus variantes de productos, haz clic en Sincronizar ahora en la barra de notificaciones. Una vez que se completa la sincronización, Gradle crea automáticamente variantes de compilación según tus tipos de compilación y variantes de productos, y les asigna nombres según <product-flavor><Build-Type>. Por ejemplo, si creaste tipos de producto "demo" y "full", y conservaste los tipos de compilación predeterminados de "debug" y "release" (lanzamiento), Gradle crea las siguientes variantes de compilación:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Para seleccionar qué variante de compilación compilar y ejecutar, dirígete a Build > Select Build Variant y selecciona una del menú. Para comenzar a personalizar cada variante de compilación con sus propias funciones y recursos, deberás crear y administrar conjuntos de orígenes, como se describe en esta página.

Cómo cambiar el ID de aplicación para variantes de compilación

Cuando compilas un APK o un AAB para tu app, las herramientas de compilación etiquetan la app con el ID de aplicación definido en el bloque defaultConfig del archivo build.gradle.kts, como se muestra en el siguiente ejemplo. Sin embargo, si deseas crear diferentes versiones de tu app para que aparezcan como fichas separadas en Google Play Store, como una versión "gratis" y otra "pro", por ejemplo, debes crear variantes de compilación independientes, cada una con un ID de aplicación diferente.

En este caso, define cada variante de compilación como una variante de producto diferente. Para cada variante del bloque productFlavors, puedes volver a definir la propiedad applicationId o, en su lugar, agregar un segmento al ID de aplicación predeterminado usando el objeto applicationIdSuffix, como se muestra aquí:

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

De esta forma, el ID de aplicación para la variante de producto "gratis" es "com.example.myapp.free".

También puedes usar el elemento applicationIdSuffix para agregar un segmento según tu tipo de compilación, como se muestra aquí:

Kotlin

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

Groovy

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

Dado que Gradle aplica la configuración de tipo de compilación después de la variante de producto, el ID de aplicación para la variante de compilación "depuración gratis" es "com.example.miapp.gratis.depuración". Esta configuración resulta útil cuando deseas tener la compilación de depuración y de lanzamiento en el mismo dispositivo, debido a que no puede haber dos apps con el mismo ID de aplicación.

Si tienes una app heredada que distribuyes con APKs en Google Play (que se creó antes de agosto de 2021) y deseas usar la misma ficha de app para distribuir varios APKs que se orienten a una configuración del dispositivo diferente (por ejemplo, el nivel de API), debes usar el mismo ID de aplicación para cada variante de compilación, pero otorgar a cada APK un versionCode diferente. Para obtener más información, consulta la sección Compatibilidad con varios APK. La publicación con AAB no se ve afectada, ya que usa un solo artefacto que utiliza un solo código de versión y un ID de aplicación de forma predeterminada.

Sugerencia: Si debes hacer referencia al ID de aplicación en el archivo de manifiesto, puedes usar el marcador de posición ${applicationId} en cualquier atributo del manifiesto. Durante una compilación, Gradle reemplaza esta etiqueta por el ID de aplicación real. Para obtener más información, consulta Cómo insertar variables de compilación en el manifiesto.

Cómo combinar diferentes variantes de productos con dimensiones de variantes

En algunos casos, es probable que desees combinar la configuración de diferentes variantes de productos. Por ejemplo, puedes crear diferentes configuraciones para los tipos de productos "completo" y "demostración" que se basen en el nivel de API. Para hacerlo, el complemento de Android para Gradle te permite crear varios grupos de variantes de productos como dimensiones de variantes.

Cuando compilas tu app, Gradle combina una configuración de variante de producto de cada dimensión de variantes que defines, junto con una configuración de tipo de compilación, para crear la variante de compilación final. Gradle no combina variantes de producto que pertenecen a la misma dimensión de variantes.

En la siguiente muestra de código, se usa la propiedad flavorDimensions para crear una dimensión de variantes "mode" y agrupar las variantes de producto "full" y "demo", así como crear una dimensión de variantes "api" para agrupar las configuraciones de variantes de productos basadas en el nivel de 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"
      ...
    }
  }
}
...

La cantidad de variantes de compilación que crea Gradle es igual al producto de la cantidad de tipos de cada dimensión de tipo y la cantidad de tipos de compilación que configuras. Cuando Gradle asigna un nombre a cada variante de compilación o artefacto correspondiente, las variantes de producto pertenecientes a la dimensión de variantes de mayor prioridad aparecen primero; luego, aparecen aquellas de dimensiones de menor prioridad y, por último, el tipo de compilación.

Con la configuración de compilación anterior como ejemplo, Gradle crea un total de 12 variantes de compilación con el siguiente esquema de nomenclatura:

  • Variante de compilación: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • APK correspondiente: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Por ejemplo:
    Variante de compilación: minApi24DemoDebug
    APK correspondiente: app-minApi24-demo-debug.apk

Además de los directorios de conjuntos de orígenes que puedes crear para cada variante de producto y de compilación en particular, también puedes crear directorios de conjuntos de orígenes para cada combinación de tipos de productos. Por ejemplo, puedes crear y agregar fuentes Java al directorio src/demoMinApi24/java/, y Gradle usa esas fuentes únicamente cuando compila una variante que combina esos dos tipos de productos.

Los conjuntos de orígenes que creas para las combinaciones de tipos de productos tienen una prioridad superior con respecto a aquellos que pertenecen a cada variante de producto en particular. Para obtener más información sobre los conjuntos de orígenes y la manera en que Gradle combina recursos, consulta la sección Cómo crear conjuntos de orígenes.

Cómo filtrar variantes

Gradle crea una variante de compilación para cada combinación posible de los tipos de productos y de compilación que configuras. Sin embargo, es posible que determinadas variantes de compilación no sean necesarias o no tengan sentido en el contexto de tu proyecto. Para quitar determinadas configuraciones de variantes de compilación, crea un filtro de variantes en el archivo build.gradle.kts de nivel de módulo.

Con la configuración de compilación de la sección anterior como ejemplo, supongamos que planeas incluir compatibilidad solo a partir del nivel de API 23 para la versión de demostración de la app. Puedes usar el bloque variantFilter a fin de filtrar todas las configuraciones de variantes de compilación que combinan las variantes de producto "minApi21" y "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)
      }
  }
}
...

Una vez que agregas un filtro de variantes a tu configuración de compilación y haces clic en Sync Now en la barra de notificaciones, Gradle omite cualquier variante de compilación que cumpla con las condiciones que especifiques. Las variantes de compilación dejan de aparecer en el menú cuando haces clic en Build > Select Build Variant en la barra de menú o Build Variants en la barra de la ventana de herramientas.

Cómo crear conjuntos de orígenes

De forma predeterminada, Android Studio crea el conjunto de orígenes y los directorios de main/ para todo lo que desees compartir entre todas tus variantes de compilación. Sin embargo, puedes crear nuevos conjuntos de orígenes para controlar exactamente los archivos que compila y empaqueta Gradle para tipos de compilación, variantes de productos y combinaciones de esas variantes (cuando se usan dimensiones de variantes) y variantes de compilación específicos.

Por ejemplo, puedes definir una funcionalidad básica en el conjunto de orígenes de main/ y usar conjuntos de orígenes de variantes de productos a los efectos de cambiar el desarrollo de la marca de tu app para diferentes clientes, o bien incluir permisos y funcionalidad de acceso especiales solo para variantes de compilación que usan el tipo de compilación de depuración.

Gradle prevé que los directorios y archivos del conjunto de orígenes estén organizados de una forma determinada, caso similar al del conjunto de orígenes main/. Por ejemplo, Gradle espera que los archivos de clase de Kotlin o Java específicos de tu tipo de compilación "debug" se ubiquen en los directorios src/debug/kotlin/ o src/debug/java/.

El complemento de Android para Gradle proporciona una tarea de Gradle útil que te muestra la manera de organizar tus archivos para cada tipo de compilación, así como variantes de producto y de compilación. Por ejemplo, en el siguiente caso del resultado de tarea, se describe el punto en el cual Gradle prevé que encontrará ciertos archivos para el tipo de compilación de "depuración":

------------------------------------------------------------
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 ver este resultado, haz lo siguiente:

  1. Haz clic en Gradle en la barra de la ventana de herramientas
  2. Navega a MyApplication > Tasks > android y haz doble clic en sourceSets.

    Para ver la carpeta Tasks, debes permitir que Gradle compile la lista de tareas durante la sincronización. Para hacerlo, sigue estos pasos:

    1. Haz clic en File > Settings > Experimental (Android Studio > Settings > Experimental en macOS).
    2. Anula la selección de Do not build Gradle task list during Gradle sync.
  3. Después de que Gradle ejecute la tarea, se abre la ventana Run para mostrar el resultado.

Nota: En el resultado de la tarea, también se muestra la manera de organizar los conjuntos de orígenes para archivos que desees usar en la ejecución de pruebas de tu app, como los conjuntos de orígenes de prueba test/ y androidTest/.

Cuando creas una nueva variante de compilación, Android Studio no crea los directorios del conjunto de orígenes, pero te ofrece algunas opciones que te ayudarán. Por ejemplo, si solo quieres crear el directorio java/ para tu tipo de compilación "debug", haz lo siguiente:

  1. Abre el panel Project y selecciona la vista Project en el menú de la parte superior del panel.
  2. Navega a MyProject/app/src/.
  3. Haz clic con el botón derecho en el directorio src y selecciona New > Directory.
  4. En el menú, debajo de Gradle Source Sets, selecciona full/java.
  5. Presiona Intro.

Android Studio crea un directorio de conjunto de orígenes para tu tipo de compilación de depuración y, luego, crea el directorio java/ dentro de este. De manera alternativa, Android Studio puede crear los directorios cuando agregues un nuevo archivo a tu proyecto para una variante de compilación específica.

Por ejemplo, a fin de crear un archivo en formato XML de valores para tu tipo de compilación "debug", haz lo siguiente:

  1. En el panel de Project, haz clic con el botón derecho en el directorio src y selecciona New > XML > Values XML File.
  2. Introduce el nombre del archivo en formato XML o mantén el nombre predeterminado.
  3. En el menú junto a Target Source Set, selecciona debug.
  4. Haz clic en Finish.

Como el tipo de compilación de "depuración" se especificó como conjunto de orígenes de destino, Android Studio genera automáticamente los directorios necesarios cuando se crea el archivo en formato XML. La estructura del directorio resultante es similar a la que se muestra en la Figura 1.

Figura 1: Nuevos directorios de conjuntos de orígenes para el tipo de compilación "debug".

Los conjuntos de orígenes activos tienen un indicador verde en su ícono que indica que están activos. El conjunto de orígenes debug tiene el sufijo [main] para mostrar que se combinará con el conjunto de orígenes main.

Usando el mismo procedimiento, también puedes crear directorios de conjunto de orígenes para variantes de productos (por ejemplo, src/demo/) o de compilación (por ejemplo, src/demoDebug/). Además, puedes crear conjuntos de fuentes de prueba para variantes de compilación específicas; por ejemplo, src/androidTestDemoDebug/. Si deseas obtener más información, lee acerca de cómo probar conjuntos de orígenes.

Cómo cambiar configuraciones predeterminadas de conjuntos de orígenes

Si tienes orígenes que no están organizados en la estructura predeterminada del archivo de conjunto de orígenes que prevé Gradle, como se describe antes en la sección Cómo crear conjuntos de orígenes, puedes usar el bloque sourceSets para cambiar la ubicación en la que Gradle espera reunir los archivos para cada componente de un conjunto de orígenes.

El bloque sourceSets debe estar en el bloque android. No es necesario que reubiques los archivos fuente; solo debes proporcionarle a Gradle las rutas de acceso, en relación con el archivo build.gradle.kts de nivel de módulo, en las que Gradle puede encontrar los archivos para cada componente del conjunto de orígenes. Para determinar los componentes que puedes configurar y confirmar si puedes asignarlos a varios archivos o directorios, consulta la referencia de la API sobre el complemento de Android para Gradle.

En la siguiente muestra de código, se asignan fuentes del directorio app/other/ a determinados componentes del conjunto de orígenes de main y se cambia el directorio raíz del conjunto de orígenes de 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'
      ...
    }
  }
}
...

Ten en cuenta que un directorio del código fuente solo puede pertenecer a un conjunto de orígenes. Por ejemplo, no puedes compartir las mismas fuentes de prueba con los conjuntos de orígenes test y androidTest. Esto se debe a que Android Studio crea módulos de IntelliJ separados para cada conjunto de orígenes y no puede admitir raíces de contenido duplicadas en conjuntos de orígenes.

Cómo realizar compilaciones con conjuntos de fuentes

Puedes usar los directorios de conjuntos de orígenes para contener el código y los recursos que deseas empaquetar solo con determinadas configuraciones. Por ejemplo, si compilas la variante de compilación "demoDebug", que es el producto combinado de una variante de producto de "demostración" y el tipo de compilación de "depuración", Gradle busca en esos directorios y les da la siguiente prioridad:

  1. src/demoDebug/ (conjunto de orígenes de variante de compilación)
  2. src/debug/ (conjunto de orígenes de tipo de compilación)
  3. src/demo/ (conjunto de orígenes de variante de producto)
  4. src/main/ (conjunto de orígenes principal)

Los conjuntos de fuentes creados para combinaciones de variantes de productos deben incluir todas las dimensiones de variantes. Por ejemplo, el conjunto de orígenes de variantes de compilación debe ser la combinación de tipo de compilación y todas las dimensiones de variantes. No se admite la fusión de código y recursos que involucran carpetas que cubren varias dimensiones de variantes, pero no todas.

Si combinas diferentes variantes de productos, la prioridad entre las variantes de productos se determina por la dimensión de variantes a la que pertenecen. Cuando se listan la dimensión de variantes con la propiedad android.flavorDimensions, las variantes de productos que pertenecen a la primera dimensión que incluyes en la lista tienen mayor prioridad que aquellas que pertenecen a la segunda dimensión, y así sucesivamente. Además, los conjuntos de fuentes que creas para las combinaciones de variantes de productos tienen una prioridad superior con respecto a los conjuntos de fuentes que pertenecen a una variante de producto en particular.

El orden de prioridad determina el conjunto de orígenes que tiene prioridad más alta cuando Gradle combina código y recursos. Debido a que es posible que el directorio del conjunto de orígenes de demoDebug/ contenga archivos específicos de esa variante de compilación, si demoDebug/ incluye un archivo que también se define en debug/, Gradle usa el archivo en el conjunto de orígenes demoDebug/. De manera similar, Gradle otorga a los archivos de los conjuntos de orígenes de tipo de compilación y variante de producto una prioridad más alta con respecto a los mismos archivos en main/. Gradle tiene en cuenta este orden de prioridad cuando se aplican las siguientes reglas de compilación:

  • Todo el código fuente de los directorios kotlin/ o java/ se compila en conjunto para generar un solo resultado.

    Nota: Para una variante determinada, Gradle genera un error de compilación si encuentra dos o más directorios de conjuntos de orígenes que hayan definido la misma clase Kotlin o Java. Por ejemplo, cuando compilas una app de depuración, no puedes definir src/debug/Utility.kt y src/main/Utility.kt, ya que Gradle realiza búsquedas en estos dos directorios durante el proceso de compilación y arroja un error "clase de duplicado". Si deseas tener diferentes versiones de Utility.kt para distintos tipos de compilación, cada tipo de compilación debe definir su propia versión del archivo y no la incluya en el conjunto de orígenes main/.

  • Los manifiestos se combinan en un solo archivo. La prioridad sigue el mismo orden que el de la lista en el ejemplo anterior. Es decir, la configuración de manifiesto para un tipo de compilación anula la configuración de manifiesto para una variante de producto, y así sucesivamente. Para obtener más información, lee documentación sobre la fusión de manifiestos.
  • Se combinan juntos los archivos de los directorios values/. Si dos archivos tienen el mismo nombre (por ejemplo, dos archivos strings.xml), se da el mismo orden de prioridad que el de la lista en el ejemplo anterior. Es decir, los valores definidos en un archivo del conjunto de orígenes de tipo de compilación anulan los valores definidos en el mismo archivo de una variante de producto, y así sucesivamente.
  • Los recursos de los directorios res/ y asset/ se empaquetan juntos. Si hay recursos con el mismo nombre definidos en dos o más conjuntos de fuentes, se les da prioridad en el mismo orden que el de la lista en el ejemplo anterior.
  • Cuando se compila la app, Gradle otorga la prioridad más baja a los recursos y manifiestos incluidos con las dependencias del módulo de biblioteca.

Cómo declarar dependencias

Para configurar una dependencia para una variante de compilación o un conjunto de orígenes de prueba específicos, asigna prefijos al nombre de la variante de compilación o del conjunto de orígenes de prueba antes de la palabra clave de configuración Implementation, como se muestra en el siguiente ejemplo:

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 obtener más información sobre las dependencias, consulta Cómo agregar dependencias de compilación.

Cómo usar la administración de dependencias con reconocimiento de variantes

El complemento de Android para Gradle 3.0.0 y versiones posteriores incluye un nuevo mecanismo de dependencias que coincide automáticamente con las variantes cuando se consume una biblioteca, lo que significa que la variante debug de una app consume automáticamente la variante debug de una biblioteca, y así sucesivamente. También funciona cuando se usan variantes (la variante freeDebug de una app consumirá la variante freeDebug de la biblioteca).

Para que el complemento coincida de manera precisa con las variantes, deberás proporcionar coincidencias alternativas, como se describe en la siguiente sección, para las instancias en las que no son posibles las coincidencias directas.

Por ejemplo, supongamos que tu app configura un tipo de compilación llamado "staging", pero una de sus dependencias de biblioteca no lo hace. Cuando el complemento intente compilar la versión "staging" de tu app, este no sabrá qué versión de la biblioteca usar y verás un mensaje de error similar al siguiente:

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

Cómo corregir errores de compilación relacionados con la coincidencia de variantes

El complemento incluye elementos de DSL para ayudarte a controlar la manera en que Gradle resuelve las situaciones en las que una coincidencia directa de variantes entre una app y una dependencia no es posible.

La siguiente es una lista de problemas relacionados con la coincidencia de dependencias con reconocimiento de variantes y cómo resolverlos mediante las propiedades de DSL:

  • Tu app incluye un tipo de compilación que una dependencia de biblioteca no contiene.

    Por ejemplo, tu app incluye un tipo de compilación "staging" (etapa de pruebas), pero una dependencia solo contiene un tipo de compilación "debug" (depuración) y "release" (lanzamiento).

    Ten en cuenta que no se produce este error cuando una dependencia de biblioteca incluye un tipo de compilación que tu app no contiene, ya que el complemento nunca le solicitará ese tipo de compilación a la dependencia.

    Usa matchingFallbacks si quieres especificar coincidencias alternativas para un tipo de compilación específico, como se muestra aquí:

    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']
            }
        }
    }
  • En el caso de una dimensión de variantes específica que existe tanto en la app como en su dependencia de biblioteca, tu app incluye tipos que la biblioteca no contiene.

    Por ejemplo, tanto tu app como sus dependencias de biblioteca incluyen una dimensión de variantes "tier" (nivel). Sin embargo, esta dimensión "tier" en la app incluye los tipos "free" (gratuito) y "paid" (pagado), pero la dependencia solo incluye los tipos "demo" (demostración) y "paid" para la misma dimensión.

    Ten en cuenta que, en el caso de una dimensión de variantes específica que existe en la app y en sus dependencias de biblioteca, no se genera un problema cuando una biblioteca incluye una variante de producto que la app no contiene, ya que el complemento nunca le solicitará ese tipo a la dependencia.

    Usa matchingFallbacks si quieres especificar coincidencias alternativas para la variante de producto "gratis" de la app, como se muestra aquí:

    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']
            }
        }
    }
  • Una dependencia de biblioteca incluye una dimensión de tipos que tu app no contiene.

    Por ejemplo, una dependencia de biblioteca incluye tipos para una dimensión "minApi", pero tu app solo incluye tipos para la dimensión "tier". Cuando quieras compilar la versión "freeDebug" de tu app, el complemento no sabrá si usar la versión "minApi23Debug" o "minApi18Debug" de tu dependencia.

    Ten en cuenta que no se genera un problema cuando tu app incluye una dimensión de variantes que una dependencia de biblioteca no contiene, ya que el complemento solo coincide con los tipos de las dimensiones que existen en la dependencia. Por ejemplo, si una dependencia no incluye una dimensión para las ABIs, la versión "freeX86Debug" de tu app usará la versión "freeDebug" de la dependencia.

    Usa missingDimensionStrategy en el bloque defaultConfig para especificar la variante predeterminada que el complemento debería seleccionar de cada una de las dimensiones faltantes, como se muestra en el siguiente ejemplo. También puedes anular las opciones que seleccionaste en el bloque productFlavors de manera que cada tipo especifique una estrategia de coincidencia diferente para una dimensión faltante.

    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 obtener más información, consulta matchingFallbacks y missingDimensionStrategy en la referencia del DSL del complemento de Android para Gradle.

Cómo configurar parámetros de firma

Gradle no firma el APK o AAB de tu compilación de lanzamiento, a menos que definas explícitamente una configuración de firma para esa compilación. Si aún no tienes una clave de firma, consulta cómo generar una clave de carga y un almacén de claves con Android Studio.

Para configurar de forma manual los ajustes de firma de tu tipo de compilación de lanzamiento mediante configuraciones de compilación de Gradle, haz lo siguiente:

  1. Crea un almacén de claves. Un almacén de claves es un archivo binario que contiene un conjunto de claves privadas. Debes conservar el almacén de claves en un lugar seguro y protegido.
  2. Crea una clave privada. Una clave privada se usa a fin de firmar tu app para su distribución y nunca se incluye con la app ni se divulga a terceros no autorizados.
  3. Agrega la configuración de firma al archivo build.gradle.kts del nivel del 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
            }
        }
    }

Nota: No es una buena práctica de seguridad incluir las contraseñas para tu clave de lanzamiento y almacén de claves dentro del archivo de compilación. En su lugar, configura el archivo de compilación para que obtenga estas contraseñas de variables de entorno o que el proceso de compilación te solicite estas contraseñas.

Para obtener estas contraseñas desde variables de entorno, usa el siguiente código:

Kotlin

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

Groovy

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

Como alternativa, puedes cargar el almacén de claves desde un archivo de propiedades local. Por motivos de seguridad, no agregues este archivo al control de código fuente. En su lugar, configúralo de forma local para cada desarrollador. Para obtener más información, consulta Cómo quitar información de firmas de los archivos de compilación.

Luego de completar este proceso, puedes distribuir tu app y publicarla en Google Play.

Advertencia: Conserva tu almacén de claves y tu clave privada en un lugar seguro y protegido, y asegúrate de contar con copias de seguridad protegidas de ellos. Si usas la firma de apps de Play y pierdes la clave de carga, puedes solicitar un restablecimiento mediante Play Console. Si publicas una app sin la firma de apps de Play (para las apps que se crearon antes de agosto de 2021) y pierdes la clave de firma de la app, no podrás publicar actualizaciones para tu app, ya que siempre deberás firmar todas sus versiones con la misma clave

Cómo firmar apps para Wear OS

Cuando publiques apps para Wear OS, deberás firmar el APK del reloj y opcionalmente el del teléfono con la misma clave. Puedes obtener más información para empaquetar y firmar apps para Wear OS en Cómo empaquetar y distribuir apps para Wear.