Uso de la función Play Delivery
Muestra el uso de Google Play Feature Delivery en Qt.

Este documento describe la funcionalidad del ejemplo de Feature Delivery. El ejemplo utiliza características soportadas desde Qt 6.11.
Play Feature Delivery puede usarse en versiones anteriores de Qt, pero crear la aplicación requiere añadir manualmente el proyecto Android y copiar los binarios Qt. Las instrucciones para ello se encuentran en el capítulo Feature Delivery en pre Qt 6.11.
¿Qué es Feature Delivery?
Play Feature Delivery es una capacidad ofrecida por Google que esencialmente permite a los desarrolladores estructurar sus proyectos de manera que la tienda Google Play pueda dividir el contenido de su aplicación en varios paquetes descargables. También permite a los desarrolladores controlar cómo se entrega el contenido a los usuarios. Estos paquetes divididos de software y contenidos se entregan en la tienda Google Play mediante Android App Bundles (AAB). La documentación para desarrolladores de Google detalla esta función.
Proyecto de ejemplo: Feature Delivery Map Loader
Esta sencilla aplicación utiliza Play Feature Delivery para servir imágenes a un usuario cuando éste las solicita. La aplicación se puede modificar fácilmente para crear una aplicación de más de 200 MB para probar los límites de tamaño de la aplicación y descargar utilizando un módulo grande de Feature Delivery.
La aplicación
La aplicación consiste en una vista que se puede arrastrar y cuatro botones.
- Al inicio se activan los botones Load Map y Show Map Info.
- Cuando se pulsa el botón Show Map Info sólo muestra que no hay información disponible.
- Load Map inicia la carga del módulo de características:
- Se muestra un pop-up de descarga, desde el pop-up se puede cancelar la descarga.
- Cuando finaliza la descarga, se elimina la ventana emergente y se activan los botones
- Change Map y Remove Map se activan.
- Remove Map solicita la desinstalación del módulo de características, deshabilitando los botones habilitados.
- Cuando se pulsa Change Map se abre una vista en la que se puede cambiar el mapa mostrado. En este ejemplo, el módulo de características consiste en una sola imagen de mapa de temática invernal.
Configuración de la carpeta de origen
- fdwintermapmodule: Módulo de características
- fdmaploader: La aplicación principal
- fdmaploader/storeloader: Interfaz JNI de entrega de funciones
Interfaz de Feature Delivery
La carpeta fdmaploader/storeloader contiene la clase de interfaz PlayStoreLoader para la API Feature Delivery. La API no está completa, pero contiene funciones relevantes para cargar y eliminar módulos de características. La carga de módulos se inicia con una llamada a PlayStoreLoader::loadModule. El estado del proceso puede supervisarse con las señales proporcionadas por PlayStoreLoaderHandler. Con la función PlayStoreLoader::getHandler se puede obtener un "handle" de las llamadas de retorno. La API de ejemplo también proporciona una forma de comprobar los módulos ya instalados con la función PlayStoreLoader::getInstalledModules y una opción para eliminar los módulos instalados con PlayStoreLoader::uninstallModules.
Este ejemplo está diseñado para que un desarrollador pueda añadir fácilmente su propio contenido para probar la entrega de funciones. Para superar el tamaño máximo de los paquetes de Play Store, se pueden añadir imágenes de mapas (no es necesario que sean imágenes de mapas, pero encaja con el tema del ejemplo.) a las carpetas de imágenes en fdmaploader y fdwintermapmodule, los nombres de las imágenes también deben añadirse al archivo images.qrc.
Bajo el capó
El módulo API consta de dos partes. La interfaz Qt (PlayStoreLoader y PlayStoreLoaderHandler) y las clases java que se encargan de llamar a Android: Interfaces de instalación dividida de Google. La interfaz Qt funciona principalmente como un passthrough para las clases java. La interfaz Qt simplifica la API de modo que, cuando se carga el módulo de características, las clases Google SplitCompat y SplitInstall y los oyentes se crean y liberan automáticamente. En este ejemplo se omiten partes de la API, como deferredInstall y el soporte de idiomas.
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)CMake función qt6_add_android_dynamic_features añade la biblioteca dinámica específica como la característica dinámica para el objetivo de la aplicación Android. requiere QT_USE_ANDROID_MODERN_BUNDLE característica que se habilite. Eso se puede hacer ya sea como una bandera en tiempo de compilación o establecer en CMakeLists. La carpeta donde reside la parte Java de la interfaz se añade a la compilación utilizando qt_add_android_dynamic_feature_java_source_dir.
En la aplicación de ejemplo, los módulos se cargan utilizando la interfaz storeloader. Las funciones Qt PlayStoreLoader y la clase PlayStoreLoaderHandler funcionan como un intermediario entre el código de ejemplo y 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); }
Las clases Java gestionan las llamadas a la API Split Install.
m_splitInstallManager.startInstall(request)
.addOnSuccessListener(sessionId -> {
PlayStoreLoaderListener listener = m_listeners.get(callId);
if (listener != null)
listener.setSessionId(sessionId);
})Creación de binarios
Utilizando la línea de comandos se puede construir un paquete AAB localmente comprobable.
Cree e introduzca un directorio de construcción al mismo nivel que el directorio fuente:
mkdir build-feature-delivery/ ; cd build-feature-delivery/
Configurar:
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
Construir:
ninja aab
Pruebas
La AAB construida puede ser probada localmente usando bundletool con el parámetro --local-testing. Android: Documentación Bundletool El comando bundletool build-apks crea un archivo apks que puede ser instalado en un dispositivo o emulador con el comando install-apks.
Comandos Bundletool utilizados
Generar APK:s a partir de un bundle:
bundletool build-apks --bundle=/path/to/bundle.aab --output=/path/to/apk/package.apks --local-testing
Instalar la aplicación en el dispositivo:
bundletool install-apks --apks=/path/to/apk/package.apks
Entregar a Play Store
Para poder subir el paquete AAB creado a Google Play Store el paquete debe estar firmado. Para ello se puede utilizar jarsigner. A continuación se muestra un ejemplo de comando jarsigner para firmar el paquete AAB. Ver Android: Documentación de Jarsigner
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 -keystore [path-to-keystore-file].keystore [path-to-aab-file].aab [alias]
Entrega de características en pre Qt 6.11
Versiones anteriores a Qt 6.11 no soportan la generación de paquetes compatibles con Google Play Store. Si no puedes usar una versión Qt 6.11 o posterior hay una forma de usar Google Play Feature Delivery, pero requiere la creación manual del proyecto Android, y copiar los binarios Qt en él. El resto del documento tiene instrucciones de cómo lograrlo. Las instrucciones pueden no ser compatibles 1 a 1 para todos los entornos, pero deberían dar buenas pistas para lograr una implementación exitosa de Feature Delivery. Se aconseja utilizar el ejemplo como base.
Módulo de características
Los módulos de características se construyen como bibliotecas normales.
- Utilice Qt Creator para crear una biblioteca compartida C++.
- Implementa características y añade recursos.
- Construir para crear binarios .so.
Feature Delivery maneja las bibliotecas C++ como bibliotecas compartidas normales que pueden o no estar disponibles en tiempo de ejecución. Antes de llamar a una biblioteca, se debe comprobar su disponibilidad.
Aplicación principal (Qt)
- Utilice Qt Creator para crear una aplicación (aquí se utilizó la plantilla de proyectoQt Quick ).
- Implemente el acceso a la biblioteca Feature Delivery. La clase central de la librería Java Google Play Feature Delivery es Android: SplitInstallManager.
- Los archivos de plantilla de Android pueden ser creados usando el botón 'Create Templates' en QtCreator Projects -> Build&Run -> [target ABI] -> Build Steps -> Build Android APK. Las plantillas se crean en la carpeta 'android' del proyecto.
- Añade los archivos Java a la carpeta
.../android/src/java/[package...]y las rutas de los archivos aCMakeLists.txt: qt_add_executable... ...[path]/[java-filename.java] ...
- En el ejemplo, se ha creado una clase Java para gestionar las llamadas y los callbacks. La clase Java sería accedida desde Qt usando JNI. Android: Request an on demand module section en la documentación de Android tiene una descripción sencilla de cómo solicitar un módulo.
- Cuando se añaden archivos Java bajo la carpeta android en el proyecto, la propiedad QT_ANDROID_PACKAGE_SOURCE_DIR debe añadirse a
CMakeLists.txt:... set_property(TARGET appFDMainApp APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android) ...
- Además, la app principal
build.gradledebe tener dependencias para la API de características: en el bloque de dependencias, sustituirimplementation 'androidx.core:core:1.13.1'por
implementation("com.google.android.play:feature-delivery:2.1.0") - Implementar el acceso a la librería proporcionada por el módulo de características. Dado que el módulo de funciones puede o no estar disponible para la aplicación principal, los módulos no se enlazan en tiempo de compilación y las llamadas al módulo deben resolverse en tiempo de ejecución. Ejemplo:
QString MapLoader::loadMapInfo() { QScopedPointer<QString> resultStr; typedef void*(*LoadMapInfoFunc)(); //Buscar si existe la biblioteca wintermapmWintermapLibrary.setFileName("fdwintermapmodule"); if (!mWintermapLibrary.load()) { qWarning() << Q_FUNC_INFO << "Failed to load library"; return QString(); } LoadMapInfoFunc loadMapInfo = (LoadMapInfoFunc) mWintermapLibrary.resolve("loadMapInfo"); if (loadMapInfo) { void* result = loadMapInfo(); resultStr.reset(static_cast<QString*>(resultado)); } else qWarning() << Q_FUNC_INFO << "Function loadMapInfo not loaded"; return *resultStr.data(); }
- Implementa la interfaz de usuario y otras partes necesarias de la aplicación principal.
Módulo de funciones (Qt)
- Utiliza Qt Creator para crear una app (se ha utilizado la plantilla de proyecto Qt C++ Library).
- Implementa las características que ofrece el módulo.
Proyecto Android (Android)
La creación de un proyecto para crear un paquete de aplicaciones Android para la entrega de funciones se basa principalmente en la documentación de Android:
Crear un proyecto Android manualmente o usando Android Studio (Usando la plantilla 'No Activity'). El proyecto se modifica para que contenga un proyecto de nivel superior y dos subproyectos, app y feature-module. La plantilla de Android Studio crea el sub-proyecto app y el feature-module puede ser añadido usando la plantilla File -> New -> New Module.
El proyecto de plantilla requiere varias modificaciones:
- Añadir el plugin Feature Delivery al nivel 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 } - Añadir módulo de características a la
settings.gradle, cambiarrootProject.namesi es necesario:... rootProject.name = "name-of-the-root-project" include(:app) include(:name-of-the-feature-module)
app - Sub-proyecto
- El proyecto Android requiere los binarios Qt del proyecto App principal:
- Copie las bibliotecas nativas en Qt build:
[build directory]/android-build/libs/[target ABI]aapp/src/main/jniLibs/[target ABI] - Copie los jars en
[build directory]/android-build/libs/aapp/libs/
- Copie las bibliotecas nativas en Qt build:
- Desde la compilación de Qt, también se copian los contenidos de la carpeta
res,AndroidManifest.xml, ylocal.propertiesa sus respectivos lugares en el proyecto Android. - Añade el archivo
feature_names.xmla la carpetaapp/src/main/res/valuesque contiene una cadena para el módulo de características:<?xml version="1.0" encoding="utf-8"?> <resources> <string name="feature_module_name">name-of-the-feature-module-here</string> </resources>
- Añade el archivo
keep.xmla la carpetaapp/src/main/res/rawque contiene:<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@string/feature_module_winter_map" tools:discard="" />
Modificaciones a los archivos de compilación del subproyecto de la aplicación
Los archivos de compilación copiados al proyecto Android necesitan algunas modificaciones.
app - Sub-proyecto
- Eliminar los bloques
buildScriptyrepositories. - El bloque Android en la aplicación principal
build.gradlerequiere algunas modificaciones: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
...
}Añade también la configuración de firma al bloque android:
android {
...
signingConfigs {
release {
storeFile file("/absolute/path/to/the/keystore.jks")
storePassword "myStorePassword"
keyAlias "myKeyAlias"
keyPassword "myKeyPassword"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
...
}
}
...
}Qt ha añadido variables de proyecto a gradle.properties. Cambia el valor de androidPackageName si es necesario.
- Elimine
package:... <manifest ... android:package... <--remove ... > ...
- Cambie
labelyandroid.app.lib_namesi es necesario:... <application ... android:label=" ... <activity ... > <meta-data android:name="android.app.lib_name" android:value=" ... /> ...
feature-module - Subproyecto
Los módulos app y feature se crean como subproyectos para el proyecto Android de nivel superior. La estructura de carpetas y archivos es similar a la del subproyecto app.
- Los binarios del módulo de características de la compilación de Qt se copian en la carpeta
[name-of-feature-module]/src/main/jniLibs/ - Como en la app principal, la carpeta
src/main/res/debería tener las carpetasxmlyvaluesque contienenqtprovider_paths.xmlylibs.xmlrespectivamente. Ambos archivos pueden copiarse desde el proyecto de la aplicación. - Si la carpeta
src/main/res/contiene carpetas drawable o mipmap y la característica no las requiere, pueden ser eliminadas. - En el módulo de características
src/main/res/valuesno debe contener el campoapp_name. En proyectos sencillos en los que strings.xml no sea necesario para otros usos, puede eliminarse. libs.xmlcontiene únicamente el nombre del módulo de características:... <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.xmlse añade al directoriosrc/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>
- El módulo de características
build.gradlees bastante similar al del proyecto app, con algunos cambios. He aquí un ejemplo: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.propertiesel archivo puede ser copiado desde el sub-proyecto app, cambia elandroidPackageNamepor el paquete feature module.
Construcción y despliegue
El paquete AAB se puede construir desde la línea de comandos utilizando gradle wrapper: ./gradlew bundle El AAB producido estará en la carpeta build/outputs/bundle/release (o debug). A continuación, la AAB puede copiarse en Google Play Store y liberarse para su prueba. Las pruebas también se puede hacer a nivel local mediante el uso de bundletool con --local-testing parámetro.
© 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.