プレイフィーチャーデリバリーの使用
Qt での Google Play Feature Delivery の使用例を紹介します。

このドキュメントでは、Feature Delivery のサンプルの機能について説明します。この例では、Qt 6.11 以降でサポートされている機能を使用しています。
Play Feature Delivery は、それ以前のバージョンの Qt でも使用できますが、アプリケーションを作成するには、手動で Android プロジェクトを追加し、Qt バイナリをコピーする必要があります。その手順は、Qt 6.11以前のFeature Deliveryの章にあります。
フィーチャーデリバリーとは何ですか?
Play Feature Delivery は Google が提供する機能で、基本的に開発者は Google Play ストアがアプリのコンテンツを複数のダウンロード可能なパッケージに分割できるようにプロジェクトを構成することができます。また、開発者は、コンテンツをユーザーに配信する方法を制御することができます。これらの分割されたソフトウェアとコンテンツのパッケージは、Android App Bundles(AAB)を使用してGoogle Playストアに配信されます。Googleの開発者向けドキュメントには、この機能の詳細が記載されている。
プロジェクトの例フィーチャーデリバリーマップローダー
このシンプルなアプリは、Play Feature Deliveryを使って、リクエストに応じて画像をユーザーに提供します。このアプリは、アプリのサイズ制限をテストするために200MBを超えるアプリを作成したり、大きなFeature Deliveryモジュールを使用してダウンロードするように簡単に変更できます。
アプリ
このアプリは、ドラッグ可能なビューと4つのボタンで構成されています。
- 起動時にLoad Map とShow Map Info ボタンが有効になります。
- Show Map Info ボタンをクリックすると、利用可能な情報がないことが表示されます。
- Load Map をクリックすると、機能モジュールのロードが開始されます:
- ダウンロードのポップアップが表示され、ポップアップからダウンロードをキャンセルできます。
- ダウンロードが完了すると、ポップアップが削除され
- Change Map とRemove Map ボタンが有効になります。
- Remove Map ボタンをクリックすると、機能モジュールのアンインストールが要求され、有効なボタンが無効になります。
- Change Map がクリックされると、表示されている地図を変更できるビューが開きます。この例では、feature moduleは冬をテーマにした地図画像1枚だけで構成されています。
ソースフォルダの設定
- fdwintermapmodule:フィーチャーモジュール
- fdmaploader:メインアプリ
- fdmaploader/storeloader:フィーチャーデリバリーのJNIインターフェイス
フィーチャーデリバリーのインターフェース
fdmaploader/storeloader フォルダーには、Feature Delivery API のインターフェースクラスPlayStoreLoader が含まれています。この API は完全ではありませんが、機能モジュールのロードと削除に関連する関数が含まれています。モジュールのロードは、PlayStoreLoader::loadModule へのコールで開始されます。プロセスのステータスは、PlayStoreLoaderHandler が提供するシグナルで監視できます。コールバックへのハンドルは、PlayStoreLoader::getHandler 関数で取得できる。サンプルAPIはまた、PlayStoreLoader::getInstalledModules 関数で既にインストールされているモジュールをチェックする方法を提供し、PlayStoreLoader::uninstallModules でインストールされているモジュールを削除するオプションを提供します。
このサンプルは、開発者が独自のコンテンツを簡単に追加して機能配信をテストできるように設計されています。Playストアの最大パッケージサイズを超えるために、地図画像(地図画像である必要はありませんが、サンプルのテーマに合っています。)をfdmaploader とfdwintermapmodule の images フォルダに追加することができます。画像名もimages.qrc ファイルに追加する必要があります。
フードの下
APIモジュールは2つの部分で構成されています。Qtインターフェイス(PlayStoreLoader とPlayStoreLoaderHandler )と、Android.NET Frameworkの呼び出しを処理するJavaクラスです:Google Split Install インターフェース。Qtインターフェースは、主にJavaクラスのパススルーとして機能します。QtインターフェースはAPIを単純化し、機能モジュールがロードされるときに、GoogleSplitCompat とSplitInstall のクラスとリスナーが自動的に作成され、解放されるようにします。この例では、deferredInstall や言語サポートなど、APIの一部が省かれている。
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関数qt6_add_android_dynamic_features 、特定のダイナミック・ライブラリをAndroidアプリケーション・ターゲットのダイナミック機能として追加します。そのためには、QT_USE_ANDROID_MODERN_BUNDLE機能が有効になっている必要があります。これは、コンパイル時のフラグとして、またはCMakeListsで設定することができます。インターフェースのJava部分が存在するフォルダは、qt_add_android_dynamic_feature_java_source_dir を使用してビルドに追加されます。
例アプリのモジュールは、storeloader インタフェースを使用してロードされます。QtPlayStoreLoader 関数とPlayStoreLoaderHandler クラスは、サンプルコードと Java の中間として動作します。
voidPlayStoreLoader::loadModule(constQStringcallId, constQStringif(callId.isEmpty()||moduleName.isEmpty())return;if(!loaderInstance->registerNatives())return;if(!loaderInstance->loader().isValid()) { { if (!loaderInstance->loader().isValid()) qCritical("StoreLoader not constructed"); return; } loaderInstance->loader().callMethod<void>("installModuleFromStore",moduleName,callId); }
Javaクラスは、Split Install APIへの呼び出しを処理します。
m_splitInstallManager.startInstall(request)
.addOnSuccessListener(sessionId -> {
PlayStoreLoaderListener listener = m_listeners.get(callId);
if (listener != null)
listener.setSessionId(sessionId);
})バイナリの作成
コマンドラインを使って、ローカルでテスト可能なAABパッケージをビルドすることができる。
ソース・ディレクトリと同じ階層にビルド・ディレクトリを作成し、入力する:
mkdir build-feature-delivery/ ; cd build-feature-delivery/
設定する:
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
ビルドする:
ninja aab
テスト
ビルドした AAB は--local-testing パラメータでbundletool を使ってローカルでテストできます。Android: Bundletool Documentationbundletoolbuild-apks コマンドで apks ファイルを作成し、install-apks コマンドでデバイスやエミュレータにインストールします。
使用する Bundletool コマンド
バンドルからAPK:sを生成する:
bundletool build-apks --bundle=/path/to/bundle.aab --output=/path/to/apk/package.apks --local-testing
デバイスにアプリをインストール
bundletool install-apks --apks=/path/to/apk/package.apks
Playストアへの配信
作成したAABパッケージをGoogle Playストアにアップロードするためには、パッケージに署名する必要があります。そのためには、jarsigner 。以下は、AABパッケージに署名するjarsignerコマンドの例です。Androidを参照してください:Jarsigner ドキュメント
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 -keystore [path-to-keystore-file].keystore [path-to-aab-file].aab [alias]
Qt 6.11以前での機能提供
Qt 6.11 より前のバージョンでは、Google Play ストアと互換性のあるパッケージ生成をサポートしていません。Qt バージョン 6.11 以降を使用できない場合、Google Play Feature Delivery を使用する方法がありますが、手動で Android プロジェクトを作成し、Qt バイナリをコピーする必要があります。このドキュメントの残りの部分に、その方法が書かれています。すべての環境に1対1で対応できるわけではありませんが、Feature Deliveryの実装を成功させるためのヒントになるはずです。この例を参考にすることをお勧めします。
機能モジュール
フィーチャー・モジュールは、通常のライブラリーと同じようにビルドします。
- Qt Creator 、C++共有ライブラリを作成する。
- 機能を実装し、リソースを追加する。
- .soバイナリを作成するためにビルドする。
Feature Deliveryでは、C++ライブラリを通常の共有ライブラリと同様に扱います。ライブラリを呼び出す前に、ライブラリが利用可能かどうかをチェックする必要があります。
メインアプリ(Qt)
- Qt Creator を使ってアプリを作成する(ここではQt Quick プロジェクトテンプレートを使用)。
- Feature Delivery ライブラリへのアクセスを実装する。Google Play Feature Delivery Javaライブラリの中心となるクラスはAndroid.SplitInstallManagerである:
- Androidのテンプレートファイルは、QtCreatorProjects -> Build&Run -> [target ABI] -> Build Steps -> Build Android APK の「Create Templates」ボタンを使って作成します
- 。
- テンプレートは、プロジェクト内の「android」フォルダに作成されます。
- Javaファイルをフォルダ
.../android/src/java/[package...]に追加し、ファイルパスをCMakeLists.txt:に追加します。qt_add_executable... ...[path]/[java-filename.java] ...
- この例では、呼び出しとコールバックを処理するJavaクラスを作成しました。Javaクラスは、JNIを使用してQtからアクセスされます。Androidの場合:AndroidドキュメントのRequest an on demand moduleセクションに、モジュールのリクエスト方法が簡単に説明されています。
- プロジェクト内のandroidフォルダの下にJavaファイルを追加する場合、QT_ANDROID_PACKAGE_SOURCE_DIRプロパティを
CMakeLists.txt: ... set_property(TARGET appFDMainApp APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android) ...
- また、メインアプリ
build.gradle、feature APIに対する依存関係を持たなければなりません:dependenciesブロックで、implementation 'androidx.core:core:1.13.1'を
に置き換えてくださいimplementation("com.google.android.play:feature-delivery:2.1.0") - featureモジュールによって提供されるライブラリへのアクセスを実装します。フィーチャー・モジュールはメイン・アプリで利用可能な場合もあれば、そうでない場合もあるため、モジュールはビルド時にリンクされず、モジュールへの呼び出しは実行時に解決する必要があります。例
QStringMapLoader::loadMapInfo() { の例。 QScopedPointer<QString>resultStr;typedef void*(*LoadMapInfoFunc)();//ウィンターマップ・ライブラリが存在するかどうか調べるmWintermapLibrary.setFileName("fdwintermapmodule");if(!mWintermapLibrary.load()) {//ウィンターマップ・ライブラリが存在するかどうか調べる qWarning() << Q_FUNC_INFO << "Failed to load library"; returnQString(); } LoadMapInfoFunc loadMapInfo=(LoadMapInfoFunc) mWintermapLibrary.resolve("loadMapInfo");if(loadMapInfo) { void*result=loadMapInfo(); resultStr.reset(static_cast<*>(result)); } else.QString*>(result)); }else qWarning() << Q_FUNC_INFO << "Function loadMapInfo not loaded"; return *resultStr.data(); }
- メインアプリのユーザーインターフェイスやその他の必要な部分を実装する。
Featureモジュール(Qt)
- Qt Creator を使用してアプリを作成する(Qt C++ Library プロジェクト・テンプレートを使用)。
- モジュールが提供する機能を実装する。
Androidプロジェクト(Android)
Feature Delivery用のAndroidアプリバンドル構築のためのプロジェクト作成は、主にAndroidのドキュメントに基づいています:
手動またはAndroid Studioを使用してAndroidプロジェクトを作成する(「No Activity」テンプレートを使用)。このプロジェクトは、トップレベルのプロジェクトと、app とfeature-module の2つのサブプロジェクトが含まれるように変更されます。Android Studioテンプレートでは、app サブプロジェクトが作成され、File -> New -> New Module テンプレートを使用してfeature-module を追加できます。
テンプレート・プロジェクトにはいくつかの修正が必要です:
- メインレベル
build.gradleに Feature Delivery プラグインを追加します: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 } settings.gradleにfeatureモジュールを追加し、必要に応じてrootProject.nameを変更する:... rootProject.name = "name-of-the-root-project" include(:app) include(:name-of-the-feature-module)
app - サブプロジェクト
- Android プロジェクトでは、メイン App プロジェクトの Qt バイナリが必要です:
- Qt build:
[build directory]/android-build/libs/[target ABI]にネイティブ・ライブラリをコピーします。app/src/main/jniLibs/[target ABI] [build directory]/android-build/libs/にある jar を以下にコピーします。app/libs/
- Qt build:
- Qt ビルドから、
resフォルダ、AndroidManifest.xml、local.propertiesの内容も Android プロジェクトのそれぞれの場所にコピーします。 - 機能モジュール用の文字列を含むファイル
feature_names.xmlをapp/src/main/res/valuesフォルダーに追加します:<?xml version="1.0" encoding="utf-8"?> <resources> <string name="feature_module_name">name-of-the-feature-module-here</string> </resources>
- ファイル
keep.xmlをapp/src/main/res/rawフォルダーに追加します:<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@string/feature_module_winter_map" tools:discard="" />
アプリのサブプロジェクトのビルドファイルの修正
Androidプロジェクトにコピーされたビルドファイルには、いくつかの修正が必要です。
app - サブプロジェクト
buildScriptとrepositoriesブロックを削除する。- メインアプリの
build.gradle、Androidブロックの修正が必要です: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
...
}また、アンドロイド・ブロックに署名設定を追加する:
android {
...
signingConfigs {
release {
storeFile file("/absolute/path/to/the/keystore.jks")
storePassword "myStorePassword"
keyAlias "myKeyAlias"
keyPassword "myKeyPassword"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
...
}
}
...
}Qt はgradle.properties にプロジェクト変数を追加しました。必要に応じて、androidPackageName の値を変更してください。
packageを削除します:... <manifest ... android:package... <--remove ... > ...
- 必要に応じて
labelとandroid.app.lib_nameを変更してください:... <application ... android:label=" ... <activity ... > <meta-data android:name="android.app.lib_name" android:value=" ... /> ...
feature-module - サブプロジェクト
アプリと機能モジュールは、トップレベルのAndroidプロジェクトのサブプロジェクトとして作成されます。フォルダとファイルの構造は、アプリのサブプロジェクトと似ています。
- Qtビルドの機能モジュール・バイナリは、Qtサブプロジェクトにコピーされます。
[name-of-feature-module]/src/main/jniLibs/ - メイン・アプリと同様に、
src/main/res/フォルダーには、qtprovider_paths.xmlとlibs.xmlをそれぞれ含むxmlとvaluesフォルダーがあるはずです。どちらのファイルも、アプリ・プロジェクトからコピーできます。 src/main/res/フォルダーに drawable や mipmap のフォルダーがあり、その機能がそれらを必要としない場合、それらを削除することができます。- フィーチャーモジュールの
src/main/res/valuesには、app_nameフィールドを含めないでください。strings.xmlが他の用途で必要とされない単純なプロジェクトでは、削除することができます。 libs.xmlには、機能モジュールの名前だけが含まれます:... <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は、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>
- 機能モジュール
build.gradleは、appプロジェクトのものとよく似ていますが、若干の変更が加えられています。以下に例を示す: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ファイルをappサブプロジェクトからコピーし、androidPackageNameをフィーチャー・モジュール・パッケージに変更します。
ビルドとデプロイ
AAB バンドルは、gradle wrapper を使ってコマンドラインからビルドできます:./gradlew bundle 作成された AAB はbuild/outputs/bundle/release (またはdebug) フォルダに入ります。このAABをGoogle Play Storeにコピーし、テスト用にリリースすることができます。--local-testing パラメータを指定してbundletool を使えば、ローカルでテストすることもできます。
© 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.