Compiler plusieurs APK

Si vous publiez votre application sur Google Play, vous devez compiler et importer un Android App Bundle (AAB). Dans ce cas, Google Play génère et diffuse automatiquement des APK optimisés pour chaque configuration d'appareil, afin que l'utilisateur télécharge uniquement le code et les ressources nécessaires pour exécuter votre application. La publication de plusieurs APK est utile si vous publiez votre application sur une plate-forme de téléchargement qui ne prend pas en charge le format AAB. Vous devez alors compiler, signer et gérer chaque APK vous-même.

Dans la mesure du possible, il est conseillé de ne compiler qu'un seul APK pour prendre en charge tous vos appareils cibles. Cependant, cela peut générer un APK très volumineux, en raison des fichiers nécessaires pour prendre en charge plusieurs densités d'écran ou interfaces binaires d'application (ABI). Pour réduire la taille de votre APK, une méthode consiste à créer plusieurs APK contenant des fichiers pour des densités d'écran ou des ABI spécifiques.

Gradle peut créer des APK distincts qui ne contiennent que le code et les ressources spécifiques à chaque densité ou ABI. Cette page vous explique comment configurer votre build afin de générer plusieurs APK. Si vous devez créer différentes versions de votre application, qui ne sont pas basées sur la densité d'écran ni sur l'ABI, vous pouvez utiliser des variantes de compilation à la place.

Configurer votre build pour plusieurs APK

Pour configurer votre build pour plusieurs APK, ajoutez un bloc splits à votre fichier build.gradle au niveau du module. À l'intérieur du bloc splits, fournissez soit un bloc density qui indique comment Gradle doit générer des APK par densité, soit un bloc abi qui indique comment il doit générer des APK par ABI. Vous pouvez fournir des blocs de densité et d'ABI. Le système de compilation créera alors un APK pour chaque combinaison de densité et d'ABI.

Configurer plusieurs APK pour les densités d'écran

Pour créer des APK distincts pour différentes densités d'écran, ajoutez un bloc density dans votre bloc splits. Dans votre bloc density, fournissez la liste des densités d'écran souhaitées et des tailles d'écran compatibles. N'utilisez la liste des tailles d'écran compatibles que si vous avez besoin d'éléments <compatible-screens> spécifiques dans le fichier manifeste de chaque APK.

Les options Gradle DSL ci-dessous permettent de configurer plusieurs APK pour les densités d'écran :

enable
Si vous définissez cet élément sur true, Gradle génère plusieurs APK en fonction des densités d'écran définies. La valeur par défaut est false.
exclude
Spécifie une liste de densités séparées par une virgule pour lesquelles Gradle ne doit pas générer d'APK distincts. Utilisez exclude si vous souhaitez générer des APK pour la plupart des densités, mais que vous devez en exclure quelques-unes qui ne sont pas prises en charge par votre application.
reset()
Efface la liste par défaut des densités d'écran. N'utilisez cette option qu'avec l'élément include pour spécifier les densités que vous souhaitez ajouter. L'extrait de code suivant définit la liste des densités sur ldpi et xxhdpi en appelant reset() pour effacer la liste, puis en utilisant include.
reset()  // Clears the default list from all densities to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities we want to generate APKs for.
include
Spécifie une liste de densités séparées par une virgule pour lesquelles Gradle doit générer des APK. N'utilisez cette option qu'avec l'élément reset() pour spécifier une liste exacte de densités.
compatibleScreens
Spécifie une liste de tailles d'écran compatibles séparées par une virgule. Un nœud <compatible-screens> correspondant est alors injecté dans le fichier manifeste pour chaque APK. Ce paramètre permet de gérer facilement les densités et les tailles d'écran dans la même section build.gradle. Cependant, l'utilisation de <compatible-screens> peut limiter les types d'appareils compatibles avec votre application. Pour découvrir d'autres méthodes pour la prise en charge de différentes tailles d'écran, consultez Compatibilité avec plusieurs écrans.

Chaque APK basé sur la densité d'écran contient une balise <compatible-screens> avec des restrictions spécifiques quant aux types d'écran compatibles avec l'APK. Aussi, même si vous publiez plusieurs APK, tous les nouveaux appareils ne correspondront pas à vos filtres APK. Par conséquent, Gradle génère toujours un APK universel supplémentaire qui contient des éléments pour toutes les densités d'écran et n'inclut pas de balise <compatible-screens>. Vous devez publier cet APK universel avec vos APK par densité afin de fournir une solution de secours pour les appareils qui ne correspondent pas aux APK contenant une balise <compatible-screens>.

L'exemple suivant génère un APK distinct pour chaque densité d'écran reprise dans la section Plage d'écrans compatibles, à l'exception de ldpi, xxhdpi et xxxhdpi. Pour ce faire, utilisez exclude afin de supprimer trois densités de la liste de toutes les densités par défaut.

Groovy

android {
  ...
  splits {

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

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

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

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

Kotlin

android {
    ...
    splits {

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

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

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

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

Pour obtenir la liste des noms de densité et de taille d'écran, consultez la section Comment prendre en charge plusieurs écrans. Pour en savoir plus sur la distribution de votre application sur des types d'écran et des appareils spécifiques, consultez Distribuer votre application sur des écrans spécifiques.

Configurer plusieurs APK pour les ABI

Pour créer des APK distincts pour différentes ABI, ajoutez un bloc abi dans votre bloc splits. Dans votre bloc abi, fournissez la liste des ABI souhaitées.

Les options Gradle DSL ci-dessous permettent de configurer plusieurs APK par ABI :

enable
Si vous définissez cet élément sur true, Gradle génère plusieurs APK en fonction des ABI définies. La valeur par défaut est false
exclude
Spécifie une liste d'ABI séparées par une virgule pour lesquelles Gradle ne doit pas générer d'APK distincts. Utilisez exclude si vous souhaitez générer des APK pour la plupart des ABI, mais que vous devez en exclure quelques-unes qui ne sont pas prises en charge par votre application.
reset()
Efface la liste par défaut des ABI. N'utilisez cette option qu'avec l'élément include pour spécifier les ABI que vous souhaitez ajouter. L'extrait de code suivant définit la liste des ABI sur x86 et x86_64 en appelant reset() pour effacer la liste, puis en utilisant include :
reset()  // Clears the default list from all ABIs to no ABIs.
include "x86", "x86_64" // Specifies the two ABIs we want to generate APKs for.
include
Spécifie une liste d'ABI séparées par une virgule pour lesquelles Gradle doit générer des APK. N'utilisez cette option qu'avec l'élément reset() pour spécifier une liste exacte d'ABI.
universalApk
Si la valeur est true, Gradle génère un APK universel en plus des APK par ABI. Un APK universel contient le code et les ressources de toutes les ABI dans un seul APK. La valeur par défaut est false. Notez que cette option n'est disponible que dans le bloc splits.abi. Lorsque vous compilez plusieurs APK en fonction de la densité d'écran, Gradle génère toujours un APK universel contenant du code et des ressources pour l'ensemble des densités.

L'exemple suivant génère un APK distinct pour chaque ABI : x86 et x86_64. Pour ce faire, utilisez l'option reset() pour commencer par une liste vide d'ABI, suivie de include avec une liste d'ABI qui recevront chacune un APK.

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      enable true

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

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

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

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

Kotlin

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

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

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

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

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

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

Pour obtenir la liste des ABI compatibles, consultez ABI compatibles.

mips, mips64 et armeabi

Par défaut, le plug-in Android pour Gradle 3.1.0 ou version ultérieure ne génère plus d'APK pour les ABI suivantes : mips, mips64 et armeabi. En effet, NDK r17 ou version ultérieure n'inclut plus ces ABI en tant que cibles compatibles.

Commencez par consulter la Google Play Console pour vous assurer que des utilisateurs téléchargent des APK de votre application qui ciblent ces ABI. Si ce n'est pas le cas, vous pouvez les retirer de votre build. Si vous souhaitez continuer à créer des APK qui ciblent ces ABI, vous devez utiliser NDK r16b ou version antérieure, définir les ABI et variantes de compilation actives, puis spécifier les ABI dans votre fichier build.gradle, comme indiqué ci-dessous :

Groovy

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

Kotlin

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

Problème connu : Si vous utilisez le plug-in Android pour Gradle 3.0.1 ou version antérieure avec NDK r17 ou version ultérieure, il se peut que l'erreur suivante soit renvoyée : Error:ABIs [mips64, armeabi, mips] are not supported for platform. En effet, les anciennes versions du plug-in incluent toujours les ABI non compatibles par défaut lorsque vous créez des APK par ABI. Pour résoudre ce problème, vous pouvez soit installer la dernière version du plug-in, soit, dans le fichier build.gradle de votre application, réinitialiser la liste des ABI par défaut du plug-in et n'inclure que les ABI compatibles souhaitées, comme indiqué ci-dessous :

Groovy

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

Kotlin

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

Projets sans code natif/C++

Le panneau Build Variants (Variantes de compilation) comprend deux colonnes : Module et Active Build Variant (Variante de compilation active). La valeur Active Build Variant (Variante de compilation active) du module détermine la variante de compilation qui sera déployée et qui sera visible dans l'éditeur.

Figure 1 : Le panneau Build Variants comporte deux colonnes pour les projets sans code natif/C++

Pour passer d'une variante à l'autre, cliquez sur la cellule Active Build Variant d'un module et sélectionnez la variante souhaitée dans le champ de liste.

Projets avec code natif/C++

Le panneau Build Variants comprend trois colonnes : Module, Active Build Variant et Active ABI (ABI active). La valeur Active Build Variant du module détermine la variante de compilation qui sera déployée et qui sera visible dans l'éditeur. Pour les modules natifs, la valeur Active ABI détermine l'ABI qui sera utilisée par l'éditeur. Cependant, elle n'a aucune incidence sur ce qui est déployé.

Figure 2 : Le panneau Build Variants ajoute la colonne Active ABI pour les projets avec du code natif/C++

Pour modifier le type de compilation ou l'ABI, cliquez sur la cellule correspondant à la colonne Active Build Variant ou Active ABI, puis sélectionnez la variante ou l'ABI souhaitée dans le champ de liste. Une nouvelle synchronisation est effectuée automatiquement. Si vous modifiez l'une des colonnes d'un module d'application ou de bibliothèque, la modification est appliquée à toutes les lignes dépendantes.

Configurer la gestion des versions

Par défaut, lorsque Gradle génère plusieurs APK, chacun d'eux dispose des mêmes informations de version, comme indiqué dans le fichier build.gradle au niveau du module. Étant donné que le Google Play Store n'autorise pas l'utilisation de plusieurs APK disposant des mêmes informations de version pour la même application, vous devez vous assurer que chaque APK possède son propre versionCode unique avant l'importation sur le Play Store.

Vous pouvez configurer votre fichier build.gradle au niveau du module pour remplacer le versionCode de chaque APK. En créant un mappage qui attribue une valeur numérique unique à chaque ABI et chaque densité pour lesquelles vous configurez plusieurs APK, vous pouvez remplacer le code de version de sortie ayant une valeur combinant le code de version défini dans le bloc defaultConfig ou productFlavors par la valeur numérique attribuée à la densité ou à l'ABI.

Dans l'exemple suivant, l'APK de l'ABI x86 aurait le versionCode 2004 et l'ABI x86_64 recevrait la valeur 3004. L'attribution de codes de version avec des incréments importants, tels que 1000, vous permet d'attribuer, par la suite, des codes de version uniques si votre application doit être mise à jour. Par exemple, si defaultConfig.versionCode effectue une itération sur 5 lors d'une mise à jour ultérieure, Gradle attribue le versionCode 2005 à l'APK x86 et 3005 à l'APK x86_64.

Conseil : Si votre build comprend un APK universel, vous devez lui attribuer un versionCode inférieur à celui de tous vos autres APK. Comme le Google Play Store installe la version de votre application qui est à la fois compatible avec l'appareil cible et qui possède le versionCode le plus élevé, l'attribution d'un versionCode plus petit à l'APK universel garantit que le Google Play Store tentera d'installer l'un de vos APK avant de revenir à l'APK universel. L'exemple de code ci-dessous gère ce cas de figure en ne remplaçant pas le versionCode par défaut d'un APK universel.

Groovy

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

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

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

import com.android.build.OutputFile

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

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

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

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

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

Kotlin

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

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

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

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

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

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

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

Vous trouverez, à la section Attribuer des codes de version, des exemples d'autres schémas de code de version.

Compiler plusieurs APK

Une fois que vous avez configuré votre fichier build.gradle au niveau du module pour compiler plusieurs APK, cliquez sur Build > Build APK (Compiler > Compiler un APK) pour compiler tous les APK du module sélectionné dans le volet Project (Projet). Gradle crée les APK pour chaque densité ou ABI dans le répertoire build/outputs/apk/ du projet.

Gradle crée un APK pour chaque densité ou ABI pour laquelle vous configurez plusieurs APK. Si vous activez plusieurs APK pour les densités et les ABI, Gradle crée un APK pour chaque combinaison de densité et d'ABI. Par exemple, l'extrait de code build.gradle suivant permet de compiler plusieurs APK pour les densités mdpi et hdpi, ainsi que pour les ABI x86 et x86_64.

Groovy

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

Kotlin

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

La sortie de l'exemple de configuration comprend les quatre APK suivants :

  • app-hdpiX86-release.apk : ne contient du code et des ressources que pour la densité hdpi et l'ABI x86.
  • app-hdpiX86_64-release.apk : ne contient du code et des ressources que pour la densité hdpi et l'ABI x86_64.
  • app-mdpiX86-release.apk : ne contient du code et des ressources que pour la densité mdpi et l'ABI x86.
  • app-mdpiX86_64-release.apk : ne contient du code et des ressources que pour la densité mdpi et l'ABI x86_64.

Lorsque vous compilez plusieurs APK en fonction de la densité d'écran, Gradle génère toujours un APK universel contenant du code et des ressources pour l'ensemble des densités, en plus des APK par densité. Lorsque vous compilez plusieurs APK en fonction de l'ABI, Gradle génère uniquement un APK qui inclut le code et les ressources de toutes les ABI si vous spécifiez universalApk true dans le bloc splits.abi de votre fichier build.gradle.

Format du nom de fichier APK

Lorsque vous compilez plusieurs APK, Gradle utilise des noms de fichiers APK selon le schéma suivant :

modulename-screendensityABI-buildvariant.apk

Les composants du schéma sont les suivants :

modulename
Spécifie le nom du module en cours de compilation.
screendensity
Si plusieurs APK sont activés pour la densité d'écran, ce composant spécifie la densité de l'écran de l'APK ; par exemple "mdpi".
ABI
Si plusieurs APK sont activés pour l'ABI, ce composant spécifie l'ABI de l'APK ; par exemple "x86". Si plusieurs APK sont activés pour la densité d'écran et l'ABI, Gradle concatène le nom de la densité avec celui de l'ABI ; par exemple "mdpiX86". Si universalApk est activé pour les APK par ABI, Gradle utilise "universal" comme partie ABI du nom de fichier de l'APK universel.
buildvariant
Spécifie la variante de compilation en cours de compilation ; par exemple "debug".

Par exemple, lorsque vous compilez un APK de densité d'écran mdpi pour la version de débogage de "myApp", le nom de fichier de l'APK est myApp-mdpi-debug.apk. La version de "myApp" configurée pour compiler plusieurs APK pour la densité d'écran mdpi et pour l'ABI x86 se voit attribuer le nom de fichier APK myApp-mdpiX86-release.apk.