Sur cette page

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é :
    1. Une fenêtre contextuelle de téléchargement s'affiche, à partir de laquelle il est possible d'annuler le téléchargement.
    2. Lorsque le téléchargement est terminé, la fenêtre contextuelle est supprimée et les boutons
    3. 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 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. 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.gradle must have dependencies for the feature API : in the dependencies block, replace
    implementation '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, modifier rootProject.name si 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] vers app/src/main/jniLibs/[target ABI]
    • Copier les bocaux dans [build directory]/android-build/libs/ vers app/libs/
  • À partir de la compilation Qt, le contenu des dossiers res, AndroidManifest.xml et local.properties est également copié dans les emplacements respectifs du projet Android.
  • Ajoutez le fichier feature_names.xml au dossier app/src/main/res/values contenant 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.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 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
build.gradle
  • Supprimer les blocs buildScript et repositories.
  • Le bloc Android dans l'application principale build.gradle nécessite quelques modifications :
    • defaultConfig
    • packagingOptions
    • dynamicFeatures
    • sourceSets
    • aaptOptions
    • dependencies
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
      ...
    }
  }
...
}
gradle.properties

Qt XML a ajouté des variables de projet à gradle.properties. Changez la valeur de androidPackageName si nécessaire.

AndroidManifest.xml
  • Supprimez package:
    ...
    <manifest
    ...
      android:package... <--remove
    ...
    >
    ...
  • Modifier label et android.app.lib_name si 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 dossiers xml et values contenant respectivement qtprovider_paths.xml et libs.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/values ne doit pas contenir le champ app_name. Dans les projets simples où strings.xml n'est pas nécessaire pour d'autres utilisations, il peut être supprimé.
  • libs.xml contient 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.xml est 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 module de fonctionnalité build.gradle est 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.properties Le fichier peut être copié à partir du sous-projet app, en remplaçant le androidPackageName par 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.

Exemple de projet @ code.qt.io

© 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.