Utilisation de la fonction de lecture Livraison
Montre l'utilisation de Google Play Feature Delivery sur Qt.

Ce document décrit les fonctionnalités de l'exemple Feature Delivery. L'exemple utilise des fonctionnalités prises en charge depuis Qt 6.11.
Play Feature Delivery peut être utilisé sur des versions plus anciennes de Qt, mais la création de l'application nécessite l'ajout manuel d'un projet Android et la copie des binaires Qt. Les instructions à ce sujet se trouvent dans le chapitre Feature Delivery on pre Qt 6.11.
Qu'est-ce que Feature Delivery ?
Play Feature Delivery est une fonctionnalité offerte par Google qui permet essentiellement aux développeurs de structurer leurs projets de manière à ce que le magasin Google Play puisse diviser le contenu de leur application en plusieurs paquets téléchargeables. Cette fonctionnalité permet également aux développeurs de contrôler la manière dont le contenu est fourni aux utilisateurs. Ces paquets de logiciels et de contenu divisés sont livrés au magasin Google Play à l'aide d'ensembles d'applications Android (AAB). La documentation de Google destinée aux développeurs détaille cette fonctionnalité.
Exemple de projet : Chargeur de cartes de livraison de fonctionnalités
Cette application simple utilise Play Feature Delivery pour servir des images à un utilisateur qui en fait la 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 de l'application et le téléchargement à l'aide d'un module Feature Delivery de grande taille.
L'application
L'application se compose d'une vue glissante 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, il indique simplement qu'aucune information n'est disponible.
- Load Map lance le chargement du module de fonctionnalité :
- Une fenêtre contextuelle de téléchargement s'affiche, à partir de laquelle il est possible d'annuler le téléchargement.
- Lorsque le téléchargement est terminé, la fenêtre contextuelle est supprimée et les boutons
- Change Map et Remove Map sont activés.
- Remove Map Le bouton "désinstaller" demande la désinstallation du module de fonctionnalité, ce qui désactive les boutons activés.
- Lorsque l'on clique sur Change Map, une vue s'ouvre, dans laquelle la carte affichée peut être modifiée. Dans cet exemple, le module de fonctionnalité consiste en une seule image de carte sur le thème de l'hiver.
Configuration du dossier source
- fdwintermapmodule: Feature Module
- fdmaploader: L'application principale
- fdmaploader/storeloader: Interface JNI de livraison de fonctionnalités
Interface Feature Delivery
Le dossier fdmaploader/storeloader contient la classe d'interface PlayStoreLoader pour l'API Feature Delivery. L'API n'est pas complète, mais elle contient des fonctions pertinentes pour le chargement et la suppression des modules de fonctionnalités. Le chargement d'un module est lancé par un appel à PlayStoreLoader::loadModule. L'état du processus peut être surveillé à l'aide de signaux fournis par PlayStoreLoaderHandler. La fonction PlayStoreLoader::getHandler permet d'obtenir une poignée pour les rappels. L'exemple d'API fournit également un moyen de vérifier les modules déjà installés avec la fonction PlayStoreLoader::getInstalledModules et une option pour supprimer les modules installés avec PlayStoreLoader::uninstallModules.
Cet exemple est conçu de manière à ce qu'un développeur puisse facilement ajouter son propre contenu pour tester la livraison de fonctionnalités. Pour dépasser la taille maximale du paquet 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 d'images dans fdmaploader et fdwintermapmodule, les noms des images doivent également être ajoutés au fichier images.qrc.
Sous le capot
Le module API se compose de deux parties. L'interface Qt XML (PlayStoreLoader et PlayStoreLoaderHandler) et les classes Java qui gèrent les appels aux interfaces Android : Google Split Install Interfaces. L'interface Qt fonctionne principalement comme un intermédiaire pour les classes Java. L'interface Qt XML simplifie l'API de sorte que lorsque le module de fonctionnalité est chargé, les classes et les récepteurs Google SplitCompat et SplitInstall sont créés et libérés automatiquement. Dans cet exemple, certaines parties de l'API sont laissées de côté, comme 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 de l'application Android. Elle nécessite que la fonctionnalité QT_USE_ANDROID_MODERN_BUNDLE soit activée. Cela peut se faire sous forme d'indicateur à la compilation ou dans les CMakeLists. Le dossier dans lequel réside la partie Java de l'interface est ajouté à la compilation à l'aide de qt_add_android_dynamic_feature_java_source_dir.
Dans l'application Exemple, les modules sont chargés à l'aide de l'interface storeloader. Les fonctions Qt PlayStoreLoader et la classe PlayStoreLoaderHandler servent d'intermédiaires entre le code de l'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
En utilisant la ligne de commande, un paquet AAB testable localement peut être construit.
Créez et entrez un répertoire de construction au même niveau que le répertoire source :
mkdir build-feature-delivery/ ; cd build-feature-delivery/
Configurer :
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=Debug
Construire :
ninja aab
Test
L'AAB construit peut être testé localement en utilisant la commande bundletool avec le paramètre --local-testing. Android : Bundletool Documentation La commande bundletool build-apks crée un fichier apks 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 APK:s à partir d'un bundle :
bundletool build-apks --bundle=/path/to/bundle.aab --output=/path/to/apk/package.apks --local-testing
Installer l'application sur l'appareil :
bundletool install-apks --apks=/path/to/apk/package.apks
Livrer au Play Store
Afin de pouvoir télécharger le paquet AAB créé sur le Google Play Store, le paquet doit être signé. Pour cela, jarsigner peut être utilisé. Vous trouverez ci-dessous un exemple de commande jarsigner pour signer le paquet AAB. Voir Android : Jarsigner Documentation
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 -keystore [path-to-keystore-file].keystore [path-to-aab-file].aab [alias]
Livraison de fonctionnalités sur pre 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 n'êtes pas en mesure d'utiliser une version 6.11 ou ultérieure de Qt, il existe un moyen d'utiliser Google Play Feature Delivery, mais cela nécessite la création manuelle du projet Android, et la copie des binaires Qt dans ce projet. Le reste du document contient des instructions pour y parvenir. Les instructions peuvent ne pas être compatibles avec tous les environnements, mais elles devraient donner de bonnes indications pour réussir la mise en œuvre de Feature Delivery. Il est conseillé d'utiliser l'exemple comme base.
Module de fonctionnalités
Les Feature Modules sont construits comme des bibliothèques normales.
- Utilisez Qt Creator pour créer une bibliothèque partagée C++.
- Implémenter des fonctionnalités et ajouter des ressources.
- Construire pour créer des binaires .so.
Feature Delivery traite les bibliothèques C++ comme des bibliothèques partagées normales qui peuvent ou non être disponibles au moment de l'exécution. Avant d'appeler une bibliothèque, il faut en vérifier la disponibilité.
Application principale (Qt)
- Utiliser Qt Creator pour créer une application (le modèle de projetQt Quick a été utilisé ici).
- Mettre en œuvre 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 en utilisant le bouton "Create Templates" sur QtCreator Projects -> Build&Run -> [target ABI] -> Build Steps -> Build Android APK. Les modèles sont créés dans le dossier "android" du projet.
- Ajoutez les fichiers Java au dossier
.../android/src/java/[package...]et les chemins d'accès aux fichiersCMakeLists.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. La classe Java est ensuite accessible à partir de Qt à l'aide de JNI. Android : Request an on demand module section in the Android documentation has a simple description of how to request a module.
- When adding Java files under the android folder in the project, QT_ANDROID_PACKAGE_SOURCE_DIR property must be added to the
CMakeLists.txt: ... set_property(TARGET appFDMainApp APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android) ...
- Also, the main app
build.gradlemust have dependencies for the feature API : in the dependencies block, replaceimplementation 'androidx.core:core:1.13.1'with
implementation("com.google.android.play:feature-delivery:2.1.0") - Implement access to the library provided by the feature module. Comme le Feature Module peut ou non être disponible pour l'application principale, les modules ne sont pas liés au moment de la construction et les appels au 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 existemWintermapLibrary.setFileName("fdwintermapmodule") ; if (!mWintermapLibrary.load()) { qWarning() << Q_FUNC_INFO << "Failed to load library"; () ; } LoadMapInfoFunc 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émenter l'interface utilisateur et les autres parties requises de l'application principale.
Module de fonctionnalités (Qt)
- Utiliser Qt Creator pour créer une application (le modèle de projet de la bibliothèque Qt C++ a été utilisé).
- Mettez en œuvre les fonctionnalités offertes par le module.
Projet Android (Android)
La création d'un projet pour la construction d'un paquet d'applications Android pour la livraison de fonctionnalités est basée sur la documentation d'Android, principalement :
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 premier niveau et deux sous-projets, app et feature-module. Le modèle Android Studio crée le sous-projet app et feature-module peut être ajouté à l'aide du modèle File -> New -> New Module.
Le projet modèle nécessite plusieurs modifications :
- Ajouter 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 } - Ajouter le module de fonctionnalités à
settings.gradle, modifierrootProject.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 :
- Copier les bibliothèques natives dans Qt build :
[build directory]/android-build/libs/[target ABI]versapp/src/main/jniLibs/[target ABI] - Copier les bocaux dans
[build directory]/android-build/libs/versapp/libs/
- Copier les bibliothèques natives dans Qt build :
- À partir de la compilation Qt, le contenu des dossiers
res,AndroidManifest.xmletlocal.propertiesest également copié dans les emplacements respectifs du projet Android. - Ajoutez le fichier
feature_names.xmlau dossierapp/src/main/res/valuescontenant une chaîne pour le module de fonctionnalité :<?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.xmlau dossierapp/src/main/res/rawcontenant :<?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 des fichiers de construction du sous-projet app
Les fichiers de construction copiés dans le projet Android nécessitent quelques modifications.
app - Sous-projet
- Supprimer les blocs
buildScriptetrepositories. - Le bloc Android dans l'application principale
build.gradlené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
...
}Ajouter également la configuration de la 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 XML a ajouté des variables de projet à gradle.properties. Changez la valeur de androidPackageName si nécessaire.
- Supprimez
package:... <manifest ... android:package... <--remove ... > ...
- Modifier
labeletandroid.app.lib_namesi nécessaire :... <application ... android:label=" ... <activity ... > <meta-data android:name="android.app.lib_name" android:value=" ... /> ...
feature-module - Sous-projet
Les modules d'application et de fonctionnalité sont créés en tant que sous-projets du projet Android de premier niveau. La structure des dossiers et des fichiers est similaire à celle du sous-projet app.
- Les binaires du module fonctionnel provenant de la compilation de Qt sont copiés dans le dossier "feature-module".
[name-of-feature-module]/src/main/jniLibs/ - Comme dans l'application principale, le dossier
src/main/res/doit contenir les dossiersxmletvaluescontenant respectivementqtprovider_paths.xmletlibs.xml. Les deux fichiers peuvent être copiés à partir du projet d'application. - Si le dossier
src/main/res/contient des dossiers drawable ou mipmap et que la fonctionnalité n'en a pas besoin, ils peuvent être supprimés. - Dans le module de fonctionnalité,
src/main/res/valuesne doit pas contenir le champapp_name. Dans les projets simples où strings.xml n'est pas nécessaire pour d'autres utilisations, il peut être supprimé. libs.xmlcontient uniquement 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épertoiresrc/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 module de fonctionnalité
build.gradleest assez similaire à celui du projet app, avec quelques changements. En 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 { resConfig "en" 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/'] } } tasks.withType(JavaCompile) { options.incremental = true } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } lintOptions { abortOnError false } // Do not compress Qt binary resources file aaptOptions { noCompress 'rcc' } } gradle.propertiesLe fichier peut être copié à partir du sous-projet app, en remplaçant leandroidPackageNamepar le paquet Feature Module.
Construction et déploiement
Le bundle AAB peut être construit à partir de la ligne de commande en utilisant le wrapper gradle : ./gradlew bundle L'AAB produit sera dans le dossier build/outputs/bundle/release (ou debug). L'AAB peut ensuite être copié sur le Google Play Store et publié pour être 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.