Configurer des variantes de compilation

Cette page explique comment configurer des variantes de compilation afin de créer différentes versions d'une application à partir d'un seul projet. Vous y découvrirez également comment gérer correctement vos dépendances et vos configurations de signature.

Chaque variante de compilation représente une version différente de votre application que vous pouvez compiler. Par exemple, vous pouvez créer une version gratuite, avec un ensemble limité de contenus, et une version payante, qui en comporte davantage. Vous pouvez également créer différentes versions de votre application pour cibler divers appareils, en fonction du niveau d'API ou d'autres paramètres.

Les variantes de compilation sont le résultat de l'utilisation, par Gradle, d'un ensemble spécifique de règles pour combiner les paramètres, le code et les ressources configurés dans vos types de compilation et vos types de produit. Bien que vous ne configuriez pas directement les variantes de compilation, vous configurez les types de compilation et les types de produit dont elles sont constituées.

Par exemple, un type de produit "demo" peut spécifier certaines fonctionnalités et configurations requises pour un appareil, telles que le code source personnalisé, des ressources et des niveaux d'API minimaux, tandis que le type de compilation "debug" applique différents paramètres de compilation et de packaging, tels que des options de débogage et des clés de signature. La variante de compilation qui combine ces deux versions est la version "demoDebug" de votre application. Elle comprend une combinaison des configurations et des ressources incluses dans le type de produit "demo", le type de compilation "debug" et l'ensemble de sources main/.

Configurer les types de compilation

Vous pouvez créer et configurer des types de compilation dans le bloc android du fichier build.gradle.kts au niveau du module. Lorsque vous créez un module, Android Studio crée automatiquement les types de compilation "debug" et "release". Bien que le type de compilation "debug" n'apparaisse pas dans le fichier de configuration de compilation, Android Studio le configure avec debuggable true. Cela vous permet de déboguer l'application sur des appareils Android sécurisés et de configurer la signature d'application avec un keystore de débogage générique.

Vous pouvez ajouter le type de compilation "debug" à votre configuration si vous souhaitez ajouter ou modifier certains paramètres. L'exemple suivant spécifie un applicationIdSuffix pour le type de compilation "debug" et configure un type de compilation "staging" qui est initialisé à l'aide des paramètres du type de compilation "debug" :

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

Remarque : Lorsque vous modifiez un fichier de configuration de compilation, Android Studio exige que vous synchronisiez votre projet avec la nouvelle configuration. Pour synchroniser votre projet, cliquez sur Sync Now (Synchroniser) dans la barre de notification qui s'affiche lorsque vous effectuez une modification. Vous pouvez aussi cliquer sur Sync Project (Synchroniser le projet) dans la barre d'outils. Si Android Studio détecte des erreurs dans votre configuration, la fenêtre Messages s'affiche pour décrire le problème.

Pour en savoir plus sur toutes les propriétés que vous pouvez configurer avec des types de compilation, consultez la documentation de référence sur BuildType.

Configurer les types de produit

Créer des types de produits et créer des types de compilation sont deux procédures semblables : vous les ajoutez au bloc productFlavors dans la configuration de compilation et vous incluez les paramètres de votre choix. Les types de produit prennent en charge les mêmes propriétés que defaultConfig, car defaultConfig appartient en fait à la classe ProductFlavor. Cela signifie que vous pouvez fournir la configuration de base de tous les types dans le bloc defaultConfig et que chaque type peut modifier n'importe quelle valeur par défaut, comme applicationId. Pour en savoir plus sur l'ID application, consultez la section Définir l'ID application.

Remarque : Vous devez toujours spécifier un nom de package à l'aide de l'attribut package dans le fichier manifeste main/. Vous devez également utiliser ce nom de package dans votre code source pour faire référence à la classe R ou pour résoudre tout enregistrement de service ou d'activité qui s'y rapporte. Cela vous permet d'utiliser applicationId pour attribuer à chaque type de produit un ID unique pour le packaging et la distribution, sans avoir à modifier votre code source.

Tous les types doivent appartenir à un groupe de types de produit nommé. Vous devez attribuer tous les types de produit à un groupe de types. Dans le cas contraire, vous obtiendrez l'erreur de compilation suivante.

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

Si un module donné ne spécifie qu'un seul groupe de types, le plug-in Android Gradle lui attribue automatiquement tous les types du module.

L'exemple de code suivant crée un groupe de types "version", et ajoute les types de produit "demo" et "full". Ces types fournissent leurs propres applicationIdSuffix et 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"
        }
    }
}

Remarque : Si vous avez une ancienne application (créée avant le mois d'août 2021) que vous distribuez à l'aide de fichiers APK sur Google Play, attribuez la même valeur applicationId à toutes les variantes et attribuez à chaque variante un versionCode différent. Vous pourrez ainsi utiliser plusieurs fichiers APK pour distribuer votre application dans Google Play. Pour distribuer différentes variantes de votre application sous la forme d'applications distinctes dans Google Play, vous devez attribuer un applicationId différent à chacune d'elles.

Après avoir créé et configuré vos types de produit, cliquez sur Sync Now (Synchroniser) dans la barre de notification. Une fois la synchronisation terminée, Gradle crée automatiquement des variantes de compilation en fonction de vos types de compilation et types de produit, et les nomme selon le format <product-flavor><Build-Type>. Par exemple, si vous avez créé les types de produit "demo" et "full", et que vous avez conservé les types de compilation "debug" et "release" par défaut, Gradle crée les variantes de compilation suivantes :

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Pour sélectionner la variante de compilation à compiler et exécuter, accédez à Build > Select Build Variant (Compilation > Sélectionner une variante de compilation), puis sélectionnez-en une dans le menu. Pour commencer à personnaliser chaque variante de compilation avec ses propres fonctionnalités et ressources, vous devez savoir comment créer et gérer des ensembles de sources, comme décrit sur cette page.

Modifier l'ID application des variantes de compilation

Lorsque vous compilez un APK ou un AAB pour votre application, les outils de compilation attribuent à l'application l'ID défini dans le bloc defaultConfig à partir du fichier build.gradle.kts, comme illustré dans l'exemple suivant. Toutefois, si vous souhaitez créer différentes versions de votre application sous forme de fiches distinctes sur le Google Play Store, comme une version "free" et une version "pro", vous devez créer des variantes de compilation distinctes ayant chacune un ID application différent.

Dans ce cas, définissez chaque variante de compilation comme un type de produit distinct. Pour chaque type de produit du bloc productFlavors, vous pouvez redéfinir la propriété applicationId ou ajouter un segment à l'ID application par défaut à l'aide de applicationIdSuffix, comme indiqué ci-dessous :

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

Dans ce cas, l'ID application du type de produit "free" est "com.example.myapp.free".

Vous pouvez également utiliser applicationIdSuffix pour ajouter un segment en fonction de votre type de compilation, comme indiqué ci-dessous :

Kotlin

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

Groovy

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

Étant donné que Gradle applique la configuration du type de compilation après le type de produit, l'ID application de la variante de compilation "free debug" est "com.example.myapp.free.debug". Cela s'avère utile lorsque vous souhaitez que "debug" et "release" soient compilés sur le même appareil, car deux applications ne peuvent pas partager le même ID.

Si vous avez une ancienne application (créée avant août 2021) que vous distribuez à l'aide de fichiers APK sur Google Play et que vous souhaitez utiliser la même fiche d'application pour distribuer plusieurs APK ciblant chacun une configuration d'appareil différente (différents niveaux d'API, par exemple), utilisez le même ID application pour chaque variante de compilation, mais attribuez un versionCode différent à chaque APK. Pour en savoir plus, consultez Prendre en charge plusieurs fichiers APK. La publication à l'aide d'AAB n'est pas affectée, car elle utilise un seul artefact qui, par défaut, emploie un seul code de version et un seul ID application.

Conseil : Si vous devez référencer l'ID application dans votre fichier manifeste, vous pouvez utiliser l'espace réservé ${applicationId} dans n'importe quel attribut de ce fichier. Lors d'une compilation, Gradle remplace cette balise par l'ID application réel. Pour en savoir plus, consultez la section Injecter des variables de compilation dans le fichier manifeste.

Combiner plusieurs types de produit avec plusieurs groupes de types

Dans certains cas, vous pouvez combiner des configurations provenant de plusieurs types de produit. Vous pouvez, par exemple, créer des configurations différentes pour les types de produit "full" et "demo" en fonction du niveau d'API. Pour ce faire, le plug-in Android Gradle vous permet de créer plusieurs groupes de types de produit.

Lorsque vous compilez votre application, Gradle combine une configuration de type de produit de chaque groupe que vous définissez avec une configuration de type de compilation pour créer la variante de compilation finale. Gradle ne combine pas des types de produit appartenant au même groupe.

L'exemple de code suivant utilise la propriété flavorDimensions pour créer un groupe de types "mode" afin de regrouper les types de produit "full" et "demo", ainsi qu'un groupe de types "api" pour regrouper les configurations de type de produit en fonction du niveau d'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"
      ...
    }
  }
}
...

Le nombre de variantes de compilation créées par Gradle est égal au nombre de types dans chaque groupe multiplié par le nombre de types de compilation que vous configurez. Lorsque Gradle nomme chaque variante de compilation ou des artefacts correspondants, les types de produit appartenant à un groupe de priorité supérieure apparaissent en premier, suivis de ceux des groupes de priorité inférieure et enfin du type de compilation.

En utilisant la configuration de compilation précédente comme exemple, Gradle crée un total de 12 variantes de compilation avec le schéma de dénomination suivant :

  • Variante de compilation : [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • APK correspondant : app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Par exemple :
    Variante de compilation : minApi24DemoDebug
    APK correspondant : app-minApi24-demo-debug.apk

En plus des répertoires d'ensembles de sources que vous pouvez créer pour chaque type de produit et variante de compilation, vous pouvez en créer pour chaque combinaison de types de produit. Par exemple, vous pouvez créer et ajouter au répertoire src/demoMinApi24/java/ des sources Java que Gradle n'utilise que lors de la compilation d'une variante qui combine ces deux types de produit.

Les ensembles de sources que vous créez pour des combinaisons de types de produit ont une priorité plus élevée que ceux appartenant à chaque type. Pour en savoir plus sur les ensembles de sources et sur la manière dont Gradle fusionne les ressources, consultez la section sur la création d'ensembles de sources.

Filtrer les variantes

Gradle crée une variante de compilation pour chaque combinaison possible de types de produit et de types de compilation que vous configurez. Cependant, il se peut que certaines variantes de compilation ne soient pas nécessaires ou n'aient pas leur place dans le contexte de votre projet. Pour supprimer certaines configurations de variantes de compilation, créez un filtre de variantes dans le fichier build.gradle.kts au niveau du module.

En prenant comme exemple la configuration de compilation de la section précédente, supposons que vous envisagiez de ne prendre en charge que les niveaux d'API 23 et supérieurs pour la version de démonstration de l'application. Vous pouvez utiliser le bloc variantFilter pour exclure toutes les configurations de variantes de compilation qui combinent les types de produit "minApi21" et "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)
      }
  }
}
...

Une fois que vous avez ajouté un filtre de variantes à votre configuration de compilation et cliqué sur Sync Now (Synchroniser maintenant) dans la barre de notification, Gradle ignore les variantes de compilation qui remplissent les conditions que vous avez spécifiées. Par conséquent, elles n'apparaissent plus dans le menu déroulant lorsque vous cliquez sur Build > Select Build Variant (Compilation > Sélectionner une variante de compilation) dans la barre de menu (ou sur Build Variants (Variantes de compilation)  dans la barre des fenêtres d'outils).

Créer des ensembles de sources

Par défaut, Android Studio crée l'ensemble de sources main/ et les répertoires pour tous les éléments que vous souhaitez partager entre toutes vos variantes de compilation. Cependant, vous pouvez créer d'autres ensembles de sources afin de contrôler précisément les fichiers que Gradle compile et empaquette pour des types de compilation, des types de produit, des combinaisons de types de produit en cas d'utilisation de groupes de types et des variantes de compilation spécifiques.

Vous pouvez, par exemple, définir des fonctionnalités de base dans l'ensemble de sources main/ et utiliser des ensembles de sources de types de produit pour modifier le branding de votre application pour différents clients, ou n'inclure des fonctionnalités de journalisation et des autorisations spéciales que pour les variantes de compilation qui utilisent le type de compilation "debug".

Gradle s'attend à ce que les fichiers et répertoires de l'ensemble de sources soient organisés d'une manière spécifique, semblable à l'ensemble de sources main/. Par exemple, Gradle s'attend à ce que les fichiers de classe Kotlin ou Java spécifiques à votre type de compilation "debug" se trouvent dans les répertoires src/debug/kotlin/ ou src/debug/java/.

Le plug-in Android Gradle fournit une tâche Gradle utile qui vous montre comment organiser vos fichiers pour chacun des types de compilation, types de produit et variantes de compilation. L'exemple de sortie de tâche ci-dessous montre à quel endroit Gradle s'attend à trouver certains fichiers pour le type de compilation "debug" :

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

Pour afficher ce résultat, procédez comme suit :

  1. Cliquez sur Gradle dans la barre des fenêtres d'outils.
  2. Accédez à MyApplication > Tasks > android (MyApplication > Tâches > android), puis double-cliquez sur sourceSets.

    Pour afficher le dossier Tasks (Tâches), vous devez autoriser Gradle à créer la liste des tâches lors de la synchronisation. Pour cela, procédez comme suit :

    1. Cliquez sur File > Settings > Experimental (Fichier > Paramètres > Expérimental) ou sur Android Studio > Settings > Experimental (Android Studio > Paramètres > Expérimental) sous macOS.
    2. Décochez la case Do not build Gradle task list during Gradle sync (Ne pas créer la liste de tâches Gradle pendant la synchronisation Gradle).
  3. Une fois que Gradle a exécuté la tâche, la fenêtre Run (Exécuter) s'ouvre et affiche le résultat.

Remarque : La sortie de la tâche vous montre également comment organiser les ensembles de sources pour les fichiers à utiliser afin d'exécuter des tests pour votre application, comme les ensembles de sources de test test/ et androidTest/.

Lorsque vous créez une variante de compilation, Android Studio ne crée pas automatiquement les répertoires d'ensembles de sources, mais vous propose plusieurs options pour vous aider à le faire. Par exemple, si vous souhaitez seulement créer le répertoire java/ pour votre type de compilation "debug" :

  1. Ouvrez le volet Project (Projet) et sélectionnez la vue Project dans le menu en haut du volet.
  2. Accédez à MyProject/app/src/.
  3. Faites un clic droit sur le répertoire src, puis sélectionnez New > Directory (Nouveau > Répertoire).
  4. Dans le menu situé sous Gradle Source Sets (Ensembles de sources Gradle), sélectionnez full/java.
  5. Appuyez sur Entrée.

Android Studio crée un répertoire d'ensemble de sources pour votre type de compilation "debug", puis crée le répertoire java/ imbriqué. Android Studio peut également créer les répertoires automatiquement lorsque vous ajoutez un fichier à votre projet pour une variante de compilation spécifique.

Par exemple, pour créer un fichier XML de valeurs pour le type de compilation "debug" :

  1. Dans le volet Project (Projet), effectuez un clic droit sur le répertoire src, puis sélectionnez New > XML > Values XML File (Nouveau > XML > Fichier XML de valeurs).
  2. Saisissez le nom du fichier XML ou conservez le nom par défaut.
  3. Dans le menu situé à côté de Target Source Set (Ensemble de sources cible), sélectionnez debug.
  4. Cliquez sur Finish (Terminer).

Étant donné que le type de compilation "debug" a été spécifié comme ensemble de sources cible, Android Studio crée automatiquement les répertoires nécessaires lors de la création du fichier XML. La structure de répertoire obtenue ressemble à celle de la figure 1.

Figure 1. Nouveaux répertoires d'ensembles de sources pour le type de compilation "debug"

Un indicateur vert s'affiche au niveau des ensembles de sources qui sont actifs. L'ensemble de sources debug porte le suffixe [main] pour indiquer qu'il sera fusionné avec l'ensemble de sources main.

Vous pouvez suivre la même procédure pour créer des répertoires d'ensembles de sources pour des types de produit, comme src/demo/, et des variantes de compilation, comme src/demoDebug/. Vous pouvez en outre créer des ensembles de sources de test qui ciblent des variantes de compilation spécifiques, comme src/androidTestDemoDebug/. Pour en savoir plus, consultez Ensembles de sources de test.

Modifier les configurations de l'ensemble de sources par défaut

Si vos sources ne sont pas organisées dans la structure de fichiers de l'ensemble de sources par défaut à laquelle s'attend Gradle (comme indiqué dans la section précédente sur la création d'ensembles de sources), vous pouvez utiliser le bloc sourceSets pour modifier l'emplacement où Gradle cherchera à collecter des fichiers pour chaque composant d'un ensemble de sources.

Le bloc sourceSets doit se trouver dans le bloc android. Il n'est pas nécessaire de déplacer les fichiers sources. Il vous suffit d'indiquer à Gradle le ou les chemins d'accès, par rapport au fichier build.gradle.kts au niveau du module, où Gradle peut trouver des fichiers pour chaque composant d'ensemble de sources. Pour savoir quels sont les composants que vous pouvez configurer et si vous pouvez les mapper sur plusieurs fichiers ou répertoires, consultez la documentation de référence de l'API du plug-in Android Gradle.

L'exemple de code suivant mappe les sources du répertoire app/other/ avec certains composants de l'ensemble de sources main et modifie le répertoire racine de l'ensemble de sources 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'
      ...
    }
  }
}
...

Notez qu'un répertoire source ne peut appartenir qu'à un seul ensemble de sources. Par exemple, vous ne pouvez pas partager les mêmes sources de test avec les ensembles de sources test et androidTest. En effet, Android Studio crée des modules IntelliJ distincts pour chaque ensemble de sources et n'accepte pas les racines de contenu en double dans les ensembles de sources.

Compiler avec des ensembles de sources

Vous pouvez utiliser des répertoires d'ensembles de sources pour stocker le code et les ressources que vous souhaitez empaqueter uniquement avec certaines configurations. Par exemple, si vous compilez la variante de compilation "demoDebug", qui est le résultat de la fusion du type de produit "demo" et du type de compilation "debug", Gradle examine ces répertoires et leur attribue la priorité suivante :

  1. src/demoDebug/ (ensemble de sources de la variante de compilation)
  2. src/debug/ (ensemble de sources du type de compilation)
  3. src/demo/ (ensemble de sources du type de produit)
  4. src/main/ (ensemble de sources principal)

Les ensembles de sources créés pour des combinaisons de types de produit doivent inclure tous les groupes de types. Par exemple, l'ensemble de sources de la variante de compilation doit être la combinaison du type de compilation et de tous les groupes de types. La fusion de code et de ressources avec des dossiers qui couvrent plusieurs groupes de types, mais pas tous, n'est pas prise en charge.

Si vous combinez plusieurs types de produit, la priorité entre les différents types est déterminée par le groupe de types auquel ils appartiennent. Lorsque vous listez des groupes de types avec la propriété android.flavorDimensions, les types de produit appartenant au premier groupe ont une priorité plus élevée que ceux appartenant au deuxième groupe, et ainsi de suite. De plus, les ensembles de sources que vous créez pour des combinaisons de types de produit ont une priorité plus élevée que ceux appartenant à un type de produit en particulier.

L'ordre de priorité détermine l'ensemble de sources ayant la priorité la plus élevée lorsque Gradle combine le code et les ressources. Étant donné que le répertoire d'ensemble de sources demoDebug/ contient probablement des fichiers spécifiques à cette variante de compilation, si demoDebug/ contient un fichier qui est également défini dans debug/, Gradle l'utilise dans l'ensemble de sources demoDebug/. De même, Gradle attribue aux fichiers des ensembles de sources du type de compilation et du type de produit une priorité plus élevée que ces mêmes fichiers dans main/. Gradle prend en compte cet ordre de priorité lors de l'application des règles de compilation suivantes :

  • Tout le code source des répertoires kotlin/ ou java/ est compilé pour générer une seule sortie.

    Remarque : Pour une variante de compilation donnée, Gradle génère une erreur de compilation s'il trouve plusieurs répertoires d'ensembles de sources ayant défini la même classe Kotlin ou Java. Par exemple, lorsque vous compilez une application de débogage, vous ne pouvez pas définir à la fois src/debug/Utility.kt et src/main/Utility.kt, car Gradle examine ces deux répertoires lors du processus de compilation et génère une erreur "duplicate class" (classe en double). Si vous voulez des versions différentes de Utility.kt pour différents types de compilation, chaque type de compilation doit définir sa propre version du fichier et ne pas l'inclure dans l'ensemble de sources main/.

  • Les fichiers manifestes sont fusionnés en un seul. La priorité est donnée dans le même ordre que celui de la liste de l'exemple précédent. Autrement dit, les paramètres de fichier manifeste d'un type de compilation remplacent ceux d'un type de produit, et ainsi de suite. Pour en savoir plus, consultez la section sur la fusion des fichiers manifestes.
  • Les fichiers des répertoires values/ sont fusionnés. Si deux fichiers portent le même nom (deux fichiers strings.xml, par exemple), la priorité est donnée dans le même ordre que celui de la liste de l'exemple précédent. En d'autres termes, les valeurs définies dans un fichier de l'ensemble de sources du type de compilation remplacent celles définies dans le même fichier dans un type de produit, et ainsi de suite.
  • Les ressources des répertoires res/ et asset/ sont empaquetées ensemble. Si des ressources portant le même nom sont définies dans plusieurs ensembles de sources, la priorité est attribuée dans le même ordre que celui de la liste de l'exemple précédent.
  • Gradle attribue aux ressources et aux fichiers manifestes inclus dans les dépendances du module de bibliothèque la priorité la plus basse lors de la compilation de l'application.

Déclarer des dépendances

Pour configurer une dépendance pour une variante de compilation ou un ensemble de sources de test spécifique, ajoutez un préfixe à son nom avant le mot clé Implementation, comme illustré dans l'exemple suivant :

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

Pour en savoir plus sur les dépendances, consultez la section Ajouter des dépendances de compilation.

Utiliser la gestion des dépendances basée sur les variantes

Le plug-in Android Gradle 3.0.0 ou version ultérieure inclut un nouveau mécanisme de dépendance qui fait automatiquement correspondre les variantes lorsqu'elles consomment une bibliothèque. Autrement dit, la variante debug d'une application consomme automatiquement la variante debug d'une bibliothèque, et ainsi de suite. Cela fonctionne également lors de l'utilisation de types : ainsi, la variante freeDebug d'une application consommera la variante freeDebug d'une bibliothèque.

Pour que le plug-in fasse correspondre précisément des variantes, vous devez fournir des solutions de remplacement correspondantes, comme décrit dans la section suivante, dans les cas où une correspondance directe ne serait pas possible.

Supposons que votre application configure un type de compilation appelé "staging", mais pas l'une de ses dépendances de bibliothèque. Lorsque le plug-in tente de compiler la version "staging" de votre application, il ne sait pas quelle version de la bibliothèque utiliser et un message d'erreur semblable à celui-ci s'affiche :

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

Résoudre les erreurs de compilation liées à la correspondance des variantes

Le plug-in inclut des éléments DSL pour vous aider à contrôler la façon dont Gradle résout les cas dans lesquels une correspondance directe s'avère impossible entre une application et une dépendance.

Vous trouverez ci-dessous une liste de problèmes liés à la mise en correspondance des dépendances basées sur les variantes, avec la façon de les résoudre à l'aide des propriétés DSL :

  • Votre application contient un type de compilation dont une dépendance de bibliothèque est dépourvue.

    Par exemple, votre application contient le type de compilation "staging", mais une dépendance n'inclut qu'un type "debug" et "release".

    Notez que le fait qu'une dépendance de bibliothèque comprenne un type de compilation que votre application n'inclut pas n'est pas un problème. En effet, le plug-in ne demande jamais ce type de compilation à partir de la dépendance.

    Utilisez matchingFallbacks pour spécifier d'autres correspondances pour un type de compilation donné, comme indiqué ici :

    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']
            }
        }
    }
  • Pour un groupe de types donné, existant à la fois dans l'application et dans sa dépendance de bibliothèque, votre application contient des types dont la bibliothèque est dépourvue.

    Par exemple, votre application et ses dépendances de bibliothèque contiennent un groupe de types "tier". Cependant, le groupe "tier" de l'application contient les types "free" et "paid", mais une dépendance inclut uniquement les types "demo" et "paid" pour le même groupe.

    Notez que, pour un groupe de types donné existant à la fois dans l'application et dans ses dépendances de bibliothèque, il n'y a pas de problème lorsqu'une bibliothèque contient un type de produit dont votre application est dépourvue. Cela est dû au fait que le plug-in ne demande jamais ce type à la dépendance.

    Utilisez matchingFallbacks pour spécifier d'autres correspondances pour le type de produit "free" de l'application, comme indiqué ici :

    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']
            }
        }
    }
  • Une dépendance de bibliothèque contient un groupe de types dont votre application est dépourvue.

    Par exemple, une dépendance de bibliothèque contient des types pour un groupe "minApi", mais votre application n'inclut des types que pour le groupe "tier". Lorsque vous souhaitez compiler la version "freeDebug" de votre application, le plug-in ne sait pas s'il doit utiliser la version "minApi23Debug" ou "minApi18Debug" de la dépendance.

    Notez que le fait que votre application comprenne un groupe de types qu'une dépendance de bibliothèque n'inclut pas n'est pas un problème. Cela est dû au fait que le plug-in ne fait correspondre que les types des groupes qui existent dans la dépendance. Par exemple, si une dépendance ne contient pas de groupe pour les ABI, la version "freeX86Debug" de votre application utilisera la version "freeDebug" de la dépendance.

    Utilisez missingDimensionStrategy dans le bloc defaultConfig pour spécifier le type de configuration par défaut que le plug-in doit sélectionner dans chaque groupe manquant, comme illustré dans l'exemple suivant. Vous pouvez également ignorer les sélections effectuées dans le bloc productFlavors afin que chaque type puisse spécifier une stratégie de correspondance différente pour un groupe manquant.

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

Pour en savoir plus, consultez matchingFallbacks et missingDimensionStrategy dans la documentation de référence sur le DSL du plug-in Android Gradle.

Configurer les paramètres de signature

Gradle ne signe pas l'APK ni l'AAB de votre build, sauf si vous définissez explicitement une configuration de signature pour cette version. Si vous ne disposez encore d'aucune clé de signature, générez une clé d'importation et un keystore à l'aide d'Android Studio.

Pour configurer manuellement les configurations de signature de votre type de compilation "build" à l'aide des configurations de compilation Gradle :

  1. Créez un keystore. Un keystore est un fichier binaire contenant un ensemble de clés privées. Vous devez conserver votre keystore en lieu sûr.
  2. Créez une clé privée. Une clé privée permet de signer votre application en vue de sa distribution. Elle n'est jamais incluse dans l'application ni divulguée à des tiers non autorisés.
  3. Ajoutez la configuration de signature au fichier build.gradle.kts au niveau du module :

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

Remarque : Pour des raisons de sécurité, il est déconseillé d'inclure les mots de passe de la clé de version et du keystore dans le fichier de compilation. À la place, configurez le fichier de compilation pour obtenir ces mots de passe auprès de variables d'environnement ou faites en sorte que le processus de compilation vous invite à les saisir.

Pour obtenir ces mots de passe auprès de variables d'environnement :

Kotlin

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

Groovy

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

Vous pouvez également charger le keystore à partir d'un fichier de propriétés locales. Pour des raisons de sécurité, n'ajoutez pas ce fichier au contrôle de la source. Configurez-la plutôt en local pour chaque développeur. Pour en savoir plus, consultez la section Supprimer les informations de signature de vos fichiers de compilation.

Une fois ce processus terminé, vous pouvez distribuer votre application et la publier sur Google Play.

Avertissement : Conservez votre keystore et votre clé privée en lieu sûr, et veillez à en effectuer des sauvegardes sécurisées. Si vous utilisez la signature d'application Play et que vous perdez votre clé d'importation, vous pouvez demander de la réinitialiser via la Play Console. Si vous publiez une application sans la signature d'application Play (pour les applications créées avant août 2021) et que vous perdez votre clé de signature d'application, vous ne pourrez pas publier de mises à jour de votre application, car vous devez toujours signer toutes les versions de l'application avec la même clé.

Signer des applications Wear OS

Lorsque vous publiez des applications Wear OS, l'APK de la montre et l'APK facultatif du téléphone doivent être signés avec la même clé. Pour en savoir plus sur le packaging et la signature des applications Wear OS, consultez Compresser et distribuer des applications Wear.