Utilisation de Play Feature Delivery
Exemples d'utilisation de Google Play Feature Delivery sur Qt.

Ce document décrit les fonctionnalités de l'exemple « Feature Delivery ». Cet exemple utilise des fonctionnalités prises en charge depuis Qt 6.11.
Play Feature Delivery peut être utilisé sur des versions antérieures de Qt, mais la création de l'application nécessite l'ajout manuel d'un projet Android et la copie des binaires Qt. Vous trouverez les instructions à ce sujet dans le chapitre « Feature Delivery sur les versions antérieures à Qt 6.11 ».
Qu'est-ce que Feature Delivery ?
Play Feature Delivery est une fonctionnalité proposée par Google qui permet essentiellement aux développeurs de structurer leurs projets de manière à ce que Google Play puisse fractionner le contenu de leur application en plusieurs paquets téléchargeables. Elle offre également aux développeurs un contrôle sur la manière dont le contenu est fourni aux utilisateurs. Ces paquets de logiciels et de contenu fractionnés sont fournis à Google Play via des Android App Bundles (AAB). La documentation pour les développeurs de Google détaille cette fonctionnalité.
Exemple de projet : Feature Delivery Map Loader
Cette application simple utilise Play Feature Delivery pour fournir des images à un utilisateur à sa demande. L’application peut être facilement modifiée pour créer une application de plus de 200 Mo afin de tester les limites de taille des applications et le téléchargement à l’aide d’un module Feature Delivery volumineux.
L'application
L'application se compose d'une vue déplaçable et de quatre boutons.
- Au démarrage, les boutons « Load Map » et « Show Map Info » sont activés.
- Lorsque l’on clique sur le bouton « Show Map Info », un message indique simplement qu’aucune information n’est disponible.
- Load Map Lance le chargement du module de fonctionnalités :
- Une fenêtre contextuelle de téléchargement s'affiche ; depuis celle-ci, le téléchargement peut être annulé.
- Une fois le téléchargement terminé, la fenêtre contextuelle disparaît et
- Change Map les boutons « Remove Map » sont activés.
- Remove Map Le bouton « Désinstaller » demande la désinstallation du module de fonctionnalités, ce qui désactive les boutons activés.
- Lorsque vous cliquez sur « Change Map », une fenêtre s’ouvre dans laquelle vous pouvez modifier la carte affichée. Dans cet exemple, le module de fonctionnalités ne contient qu’une seule image de carte sur le thème de l’hiver.
Configuration du dossier source
- fdwintermapmodule: module de fonctionnalités
- fdmaploader: l'application principale
- fdmaploader/storeloader: interface JNI de diffusion des fonctionnalités
Interface de diffusion des fonctionnalités
Le dossier « fdmaploader/storeloader » contient les classes d’interface PlayStoreLoader pour l’API de livraison de fonctionnalités. L’API n’est pas complète, mais contient les fonctions nécessaires au chargement et à la suppression des modules de fonctionnalités. Le chargement d’un module est lancé par un appel à la fonction PlayStoreLoader::loadModule. L’état du processus peut être surveillé grâce aux signaux fournis par la fonction PlayStoreLoaderHandler. Un descripteur vers les callbacks peut être obtenu via la fonction PlayStoreLoader::getHandler. L'API d'exemple permet également de vérifier les modules déjà installés à l'aide de la fonction ` PlayStoreLoader::getInstalledModules ` et offre une option pour supprimer les modules installés à l'aide de ` PlayStoreLoader::uninstallModules`.
Cet exemple est conçu pour permettre à un développeur d’ajouter facilement son propre contenu afin de tester la fonctionnalité Feature Delivery. Pour dépasser la taille maximale autorisée pour les paquets sur le Play Store, des images de carte (il n’est pas nécessaire qu’il s’agisse d’images de carte, mais cela correspond au thème de l’exemple) peuvent être ajoutées aux dossiers « images » situés dans fdmaploader et fdwintermapmodule; les noms des images doivent également être ajoutés au fichier images.qrc.
En coulisses
Le module API se compose de deux parties : l’interface Qt (PlayStoreLoader et PlayStoreLoaderHandler) et les classes Java qui gèrent l’appel des interfaces Android : Google Split Install. L’interface Qt sert principalement de passerelle pour les classes Java. L’interface Qt simplifie l’API de telle sorte que, lors du chargement du module de fonctionnalités, les classes et les écouteurs Google SplitCompat et SplitInstall sont créés et libérés automatiquement. Dans cet exemple, certaines parties de l’API ont été omises, telles que deferredInstall et la prise en charge des langues.
Qt creates and builds package suitable for Google Play deployment using qt6_add_android_dynamic_features when it is defined CMakeLists.
qt6_add_android_dynamic_features(${target_name}
FEATURE_TARGETS fdwintermapmodule)La fonction CMake qt6_add_android_dynamic_features ajoute la bibliothèque dynamique spécifique en tant que fonctionnalité dynamique pour la cible d'application Android. Elle nécessite que QT_ANDROID_GRADLE_MULTI_MODULE soit activé. Cela peut se faire soit via un indicateur de compilation, soit en le définissant dans le fichier CMakeLists. Le dossier contenant la partie Java de l'interface est ajouté à la compilation à l'aide de ` qt_add_android_dynamic_feature_java_source_dir`.
Dans l’application d’exemple, les modules sont chargés à l’aide de l’interface storeloader. Les fonctions Qt PlayStoreLoader et la classe PlayStoreLoaderHandler servent d’intermédiaire entre le code d’exemple et Java.
void PlayStoreLoader::loadModule(const QString& callId,
const QString&moduleName)
{
if (callId.isEmpty()|| moduleName.isEmpty())
return;
if (!loaderInstance->registerNatives())
return;
if (!loaderInstance->loader().isValid()) {
qCritical("StoreLoader not constructed");
return;
}
loaderInstance->loader().callMethod<void>("installModuleFromStore", moduleName, callId);
}Les classes Java gèrent les appels à l'API Split Install.
m_splitInstallManager.startInstall(request)
.addOnSuccessListener(sessionId -> {
PlayStoreLoaderListener listener = m_listeners.get(callId);
if (listener != null)
listener.setSessionId(sessionId);
})Création de binaires
À l'aide de la ligne de commande, il est possible de générer un paquet AAB pouvant être testé localement.
Créez un répertoire de compilation et placez-le au même niveau que le répertoire source :
mkdir build-feature-delivery/ ; cd build-feature-delivery/Configuration :
path-to-qt-version/path-to-abi/bin/qt-cmake -GNinja -B . -S ../feature-delivery/ -DQT_USE_TARGET_ANDROID_BUILD_DIR=ON -DCMAKE_BUILD_TYPE=DebugCompiler :
ninja aabTests
L'AAB compilé peut être testé localement à l'aide de la commande ` bundletool ` avec le paramètre ` --local-testing `. Android : Documentation de Bundletool La commande `bundletool build-apks ` crée un fichier APK qui peut ensuite être installé sur un appareil ou un émulateur à l'aide de la commande ` install-apks `.
Commandes Bundletool utilisées
Générer des fichiers APK à partir d’un bundle :
bundletool build-apks --bundle=/path/to/bundle.aab --output=/path/to/apk/package.apks --local-testingInstaller l'application sur l'appareil :
bundletool install-apks --apks=/path/to/apk/package.apksPublication sur le Play Store
Pour pouvoir publier le package AAB créé sur le Google Play Store, celui-ci doit être signé. Pour cela, vous pouvez utiliser l'jarsigner. Vous trouverez ci-dessous un exemple de commande jarsigner permettant de signer le package AAB. Voir la documentation Android : Jarsigner
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 -keystore [path-to-keystore-file].keystore [path-to-aab-file].aab [alias]Mise à disposition des fonctionnalités sur les versions antérieures à Qt 6.11
Les versions antérieures à Qt 6.11 ne prennent pas en charge la génération de paquets compatibles avec le Google Play Store. Si vous ne pouvez pas utiliser une version de Qt 6.11 ou ultérieure, il existe un moyen d’utiliser la fonctionnalité « Feature Delivery » de Google Play, mais cela nécessite la création manuelle du projet Android et la copie des binaires Qt dans celui-ci. La suite de ce document contient des instructions pour y parvenir. Ces instructions ne sont peut-être pas parfaitement compatibles avec tous les environnements, mais elles devraient fournir de bons conseils pour réussir la mise en œuvre de Feature Delivery. Il est conseillé de s'appuyer sur l'exemple fourni.
Module de fonctionnalités
Les modules de fonctionnalités sont compilés comme des bibliothèques classiques.
- Utilisez Qt Creator pour créer une bibliothèque partagée C++.
- Implémentez les fonctionnalités et ajoutez des ressources.
- Compilez pour créer des binaires .so.
Feature Delivery traite les bibliothèques C++ comme des bibliothèques partagées normales, qui peuvent être disponibles ou non au moment de l'exécution. Avant d'appeler une bibliothèque, il faut vérifier si celle-ci est disponible.
Application principale (Qt)
- Utilisez Qt Creator pour créer une application (le modèle de projetQt Quick a été utilisé ici).
- Implémentez l’accès à la bibliothèque Feature Delivery. La classe centrale de la bibliothèque Java Google Play Feature Delivery est Android: SplitInstallManager.
- Les fichiers modèles Android peuvent être créés à l’aide du bouton « Create Templates » (Créer des modèles) dans QtCreator Projects -> Build&Run -> [target ABI] -> Build Steps -> Build Android APK. Les modèles sont créés dans le dossier « android » du projet.
- Ajoutez des fichiers Java au dossier
.../android/src/java/[package...]et des chemins d’accès aux fichiers àCMakeLists.txt:qt_add_executable... ...[path]/[java-filename.java] ... - Dans l’exemple, une classe Java a été créée pour gérer les appels et les rappels. Cette classe Java serait ensuite accessible depuis Qt via JNI. La section « Android : Demander un module à la demande » de la documentation Android contient une description simple de la procédure à suivre pour demander un module.
- Lors de l'ajout de fichiers Java dans le dossier « android » du projet, la propriété QT_ANDROID_PACKAGE_SOURCE_DIR doit être ajoutée à la configuration «
CMakeLists.txt» :... set_property(TARGET appFDMainApp APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android) ... - De plus, l’application principale
build.gradledoit disposer des dépendances pour l’API du module de fonctionnalité : dans le bloc « dependencies », remplacezimplementation 'androidx.core:core:1.13.1'par
implementation("com.google.android.play:feature-delivery:2.1.0") - Implémentez l’accès à la bibliothèque fournie par le module de fonctionnalité. Étant donné que le module de fonctionnalité peut être disponible ou non pour l’application principale, les modules ne sont pas liés au moment de la compilation et les appels vers le module doivent être résolus au moment de l’exécution. Exemple :
QString MapLoader::loadMapInfo() { QScopedPointer<QString> resultStr; typedef void* (*LoadMapInfoFunc)(); // Vérifier si la bibliothèque wintermap existe mWintermapLibrary.setFileName("fdwintermapmodule"); if (!mWintermapLibrary.load()) { qWarning() << Q_FUNC_INFO << "Failed to load library"; retour QString(); } LoadMapInfoFunc loadMapInfo = ( LoadMapInfoFunc) mWintermapLibrary.resolve("loadMapInfo"); if (loadMapInfo) { void* result = loadMapInfo(); resultStr.reset(static_cast<QString*>(result)); } else qWarning() << Q_FUNC_INFO << "Function loadMapInfo not loaded"; return *resultStr.data(); } - Implémentez l'interface utilisateur et les autres éléments nécessaires de l'application principale.
Module de fonctionnalités (Qt)
- Utilisez Qt Creator pour créer une application (le modèle de projet « Bibliothèque Qt C++ » a été utilisé).
- Implémentez les fonctionnalités proposées par le module.
Projet Android (Android)
La création d'un projet permettant de générer un bundle d'application Android pour Feature Delivery s'appuie sur la documentation Android, notamment :
Créez un projet Android manuellement ou à l'aide d'Android Studio (en utilisant le modèle « No Activity »). Le projet est modifié de manière à contenir un projet de niveau supérieur et deux sous-projets : app et feature-module. Le modèle d'Android Studio crée le sous-projet app, tandis que le sous-projet feature-module peut être ajouté à l'aide du modèle File -> New -> New Module.
Le projet de modèle nécessite plusieurs modifications :
- Ajoutez le plugin Feature Delivery au niveau principal
build.gradle:plugins { id 'com.android.application' version '8.5.2' apply false id 'com.android.dynamic-feature' version '8.5.2' apply false id 'com.android.library' version '8.5.2' apply false } - Ajoutez le module de fonctionnalité à l'
settings.gradle, modifiez l'rootProject.namesi nécessaire :... rootProject.name = "name-of-the-root-project" include(:app) include(:name-of-the-feature-module)
app - Sous-projet
- Le projet Android nécessite les binaires Qt du projet « Main App » :
- Copiez les bibliothèques natives de la compilation Qt :
[build directory]/android-build/libs/[target ABI]versapp/src/main/jniLibs/[target ABI] - Copier les fichiers JAR de
[build directory]/android-build/libs/versapp/libs/
- Copiez les bibliothèques natives de la compilation Qt :
- À partir de la compilation Qt, le contenu du dossier
res, ainsi que les fichiersAndroidManifest.xmletlocal.properties, sont également copiés vers leurs emplacements respectifs dans le projet Android. - Ajoutez le fichier «
feature_names.xml» dans le dossier «app/src/main/res/values» ; ce fichier contient une chaîne de caractères pour le module de fonctionnalités :<?xml version="1.0" encoding="utf-8"?> <resources> <string name="feature_module_name">name-of-the-feature-module-here</string> </resources> - Ajoutez le fichier «
keep.xml» au dossier «app/src/main/res/raw» contenant :<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@string/feature_module_winter_map" tools:discard="" />
Modifications apportées aux fichiers de compilation du sous-projet de l'application
Les fichiers de compilation copiés dans le projet Android nécessitent quelques modifications.
app - Sous-projet
- Supprimez les blocs «
buildScript» et «repositories». - Le bloc Android dans
build.gradlede l'application principale nécessite quelques modifications :defaultConfigpackagingOptionsdynamicFeaturessourceSetsaaptOptionsdependencies
android {
...
defaultConfig {
...
applicationId "your-project-name-here"
...
}
packagingOptions.jniLibs.useLegacyPackaging true
dynamicFeatures = [":your-dynamic-feature-name-here"]
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qtAndroidDir + '/res', 'res']
resources.srcDirs = ['resources']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['src/main/jniLibs/']
}
}
// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}
...
}
dependencies {
...
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'com.google.android.play:feature-delivery:2.1.0'
implementation libs.material
...
}Ajoutez également la configuration de signature au bloc Android :
android {
...
signingConfigs {
release {
storeFile file("/absolute/path/to/the/keystore.jks")
storePassword "myStorePassword"
keyAlias "myKeyAlias"
keyPassword "myKeyPassword"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
...
}
}
...
}Qt a ajouté des variables de projet à gradle.properties. Modifiez la valeur de androidPackageName si nécessaire.
- Supprimez
package:... <manifest ... android:package... <--remove ... > ... - Modifiez
labeletandroid.app.lib_namesi nécessaire :... <application ... android:label=" ... <activity ... > <meta-data android:name="android.app.lib_name" android:value=" ... /> ...
module de fonctionnalités - Sous-projet
Les modules d'application et de fonctionnalité sont créés en tant que sous-projets du projet Android de niveau supérieur. La structure des dossiers et des fichiers est similaire à celle du sous-projet d'application.
- Les binaires du module de fonctionnalité issus de la compilation Qt sont copiés dans le
[name-of-feature-module]/src/main/jniLibs/ - Comme dans l’application principale, le dossier «
src/main/res/» doit contenir les dossiers «xml» et «values», contenant respectivement les fichiers «qtprovider_paths.xml» et «libs.xml». Ces deux fichiers peuvent être copiés depuis le projet d’application. - Si le dossier
src/main/res/contient des dossiers drawable ou mipmap et que la fonctionnalité n'en a pas besoin, ceux-ci peuvent être supprimés. - Dans le module de fonctionnalité, le fichier «
src/main/res/values» ne doit pas contenir le champ «app_name». Dans les projets simples où le fichier « strings.xml » n’est pas nécessaire à d’autres fins, il peut être supprimé. libs.xmlne contient que le nom du module de fonctionnalité :... <array name="load_local_libs"> <item>name-of-the-feature-module-here</item> </array> <string name="static_init_classes"></string> <string name="use_local_qt_libs">0</string> <string name="bundle_local_qt_libs">0</string> ...AndroidManifest.xmlest ajouté au répertoire «src/main/» :<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:dist="http://schemas.android.com/apk/distribution"> <dist:module dist:instant="false" dist:title="@string/feature_module_title_string"> <dist:delivery> <dist:on-demand /> </dist:delivery> <dist:fusing dist:include="false" /> </dist:module> <!-- This feature module does contain code. --> <application android:hasCode="true"/> </manifest>- Le fichier `
build.gradle` du module de fonctionnalité est assez similaire à celui du projet d'application, à quelques modifications près. Voici un exemple :plugins { id 'com.android.dynamic-feature' } dependencies { implementation project(':app') implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation 'com.google.android.play:feature-delivery:2.1.0' } android { namespace = androidPackageName compileSdk = androidCompileSdkVersion ndkVersion androidNdkVersion // Extract native libraries from the APK packagingOptions.jniLibs.useLegacyPackaging true defaultConfig { minSdkVersion qtMinSdkVersion targetSdkVersion qtTargetSdkVersion } sourceSets { main { manifest.srcFile 'src/main/AndroidManifest.xml' resources.srcDirs = ['resources'] renderscript.srcDirs = ['src'] assets.srcDirs = ['assets'] jniLibs.srcDirs = ['src/main/jniLibs/'] } } compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } // Do not compress Qt binary resources file aaptOptions { noCompress 'rcc' } } gradle.propertiesLe fichier peut être copié depuis le sous-projet « app » ; il suffit de remplacer «androidPackageName» par le nom du paquet du module de fonctionnalité.
Compilation et déploiement
Le bundle AAB peut être généré depuis la ligne de commande à l'aide du wrapper Gradle : ` ./gradlew bundle`. Le fichier AAB généré se trouvera dans le dossier ` build/outputs/bundle/release ` (ou ` debug`). L'AAB peut ensuite être copié sur le Google Play Store et publié à des fins de test. Les tests peuvent également être effectués localement en utilisant ` bundletool ` avec le paramètre ` --local-testing `.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.