このページでは

「Play Feature Delivery」の使用方法

Qt での Google Play 機能配信の利用例を紹介します。

「地図を読み込む」「地図を変更する」「地図を削除する」ボタンを備えた地図読み込みアプリ

このドキュメントでは、Feature Delivery のサンプルアプリケーションの機能について説明します。このサンプルでは、Qt 6.11 以降でサポートされている機能を使用しています。
Play Feature Deliveryは古いバージョンのQtでも使用できますが、アプリケーションを作成するには、Androidプロジェクトを手動で追加し、Qtバイナリをコピーする必要があります。その手順については、「Qt 6.11以前のFeature Delivery」の章に記載されています。

Feature Delivery とは?

Play Feature Deliveryは、Googleが提供する機能であり、基本的に開発者がプロジェクトを構成することで、Google Playストアがアプリのコンテンツを複数のダウンロード可能なパッケージに分割できるようにします。また、開発者はコンテンツがユーザーに配信される方法を制御することもできます。これらの分割されたソフトウェアおよびコンテンツパッケージは、Android App Bundles(AAB)を使用してGoogle Playストアに配信されます。Googleの開発者向けドキュメントに、この機能の詳細が記載されています。

サンプルプロジェクト:Feature Delivery Map Loader

このシンプルなアプリは、Play Feature Delivery を使用して、リクエストがあった際にユーザーに画像を配信します。このアプリは簡単に修正でき、アプリのサイズ制限をテストするために 200MB を超えるアプリを作成したり、大容量の Feature Delivery モジュールを使用してダウンロードを行ったりすることができます。

アプリについて

このアプリケーションは、ドラッグ可能なビューと4つのボタンで構成されています。

  • 起動時には、「Load Map 」と「Show Map Info 」のボタンが有効になっています。
  • 「Show Map Info 」ボタンをクリックすると、利用可能な情報がないことが表示されるだけです。
  • Load Map 機能モジュールの読み込みを開始します:
    1. ダウンロードのポップアップが表示され、このポップアップからダウンロードをキャンセルできます。
    2. ダウンロードが完了するとポップアップが閉じられ、
    3. Change Map 「Remove Map 」ボタンが有効になります。
  • Remove Map ボタンはフィーチャーモジュールのアンインストールを要求し、有効だったボタンを無効にします。
  • 「Change Map 」をクリックすると、表示されている地図を変更できるビューが開きます。この例では、フィーチャーモジュールは冬をテーマにした地図画像1枚のみで構成されています。
ソースフォルダの設定
  • fdwintermapmodule: フィーチャーモジュール
  • fdmaploader: メインアプリ
  • fdmaploader/storeloader: フィーチャー配信用 JNI インターフェース
フィーチャー配信インターフェース

fdmaploader/storeloader フォルダには、機能配信API用のインターフェースクラスPlayStoreLoader が含まれています。このAPIはまだ完全ではありませんが、機能モジュールの読み込みおよび削除に関連する関数が含まれています。モジュールの読み込みは、PlayStoreLoader::loadModule の呼び出しによって開始されます。プロセスの状態は、PlayStoreLoaderHandler が提供するシグナルで監視できます。コールバックへのハンドルは、PlayStoreLoader::getHandler 関数で取得できます。 また、このサンプルAPIでは、PlayStoreLoader::getInstalledModules 関数を使用してすでにインストールされているモジュールを確認したり、PlayStoreLoader::uninstallModules 関数を使用してインストール済みのモジュールを削除したりすることも可能です。

このサンプルは、開発者が独自のコンテンツを簡単に追加して「Feature Delivery」をテストできるように設計されています。Play Storeのパッケージサイズ上限を超える場合、地図画像(必ずしも地図画像である必要はありませんが、このサンプルのテーマに合っています)をfdmaploader およびfdwintermapmodule 内のimagesフォルダに追加できます。その際、画像名はimages.qrc ファイルにも追加する必要があります。

内部の仕組み

APIモジュールは2つの部分で構成されています。Qtインターフェース(PlayStoreLoader およびPlayStoreLoaderHandler )と、Androidの「Google Split Install Interfaces」への呼び出しを処理するJavaクラスです。Qtインターフェースは、主にJavaクラスへのパススルーとして機能します。 QtインターフェースはAPIを簡略化しており、機能モジュールが読み込まれると、GoogleのSplitCompat および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_ANDROID_GRADLE_MULTI_MODULEが有効になっている必要があります。これは、コンパイル時のフラグとして指定するか、CMakeListsで設定することで有効にできます。 インターフェースのJava部分が格納されているフォルダは、qt_add_android_dynamic_feature_java_source_dir を使用してビルドに追加されます。

サンプルアプリでは、storeloader インターフェースを使用してモジュールが読み込まれます。Qt XMLのPlayStoreLoader 関数およびPlayStoreLoaderHandler クラスは、サンプルコードとJavaの間の中継役として機能します。

voidPlayStoreLoader::loadModule(constQString&callId,
    constQString&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);
}

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は、`bundletool `コマンドに`--local-testing `パラメータを指定することで、ローカルでテストできます。Android: BundleToolドキュメント`bundletoolbuild-apks `コマンドはapkファイルを作成し、そのapkファイルは`install-apks `コマンドを使用してデバイスやエミュレータにインストールできます。

使用したBundletoolコマンド

バンドルからAPKを生成する:

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 フィーチャーデリバリーを利用する方法がありますが、その場合は Android プロジェクトを手動で作成し、Qt バイナリをそのプロジェクトにコピーする必要があります。 このドキュメントの残りの部分では、その方法について説明します。この手順はすべての環境で完全に適用できるとは限りませんが、Feature Deliveryを正常に実装するための有益なヒントとなるはずです。サンプルを基に作業を行うことをお勧めします。

フィーチャーモジュール

フィーチャーモジュールは、通常のライブラリと同様にビルドされます。

  • Qt Creator を使用して、C++ 共有ライブラリを作成します。
  • 機能を実装し、リソースを追加します。
  • ビルドして .so バイナリを作成します。

Feature Delivery では、C++ ライブラリを、実行時に利用可能である場合とそうでない場合がある通常の共有ライブラリと同様に扱います。ライブラリを呼び出す前に、その利用可能状態を確認する必要があります。

メインアプリ(Qt)
  • Qt Creator を使用してアプリを作成します(ここでは「Qt Quick 」プロジェクトテンプレートを使用しました)。
  • Feature Delivery ライブラリへのアクセスを実装します。Google Play Feature Delivery Java ライブラリの中核となるクラスはAndroid: SplitInstallManager です。
  • Android テンプレートファイルは、QtCreator の「Create Templates」ボタンを使用して作成できます(Projects -> Build&Run -> [target ABI] -> Build Steps -> Build Android APK )。 テンプレートは、プロジェクト内の「android」フォルダに作成されます。
  • Java ファイルを.../android/src/java/[package...] フォルダに追加し、ファイルパスをCMakeLists.txt に登録します:
    qt_add_executable...
    ...[path]/[java-filename.java]
    ...
  • この例では、呼び出しとコールバックを処理するための Java クラスが作成されました。この Java クラスは、JNI を使用して Qt からアクセスされます。Android:Androidドキュメントの「オンデマンドモジュールのリクエスト」セクションには、モジュールをリクエストする方法について簡単な説明があります。
  • プロジェクトの「android」フォルダ下にJavaファイルを追加する際は、CMakeLists.txtにQT_ANDROID_PACKAGE_SOURCE_DIRプロパティを追加する必要があります:
    ...
    set_property(TARGET appFDMainApp APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
                 ${CMAKE_CURRENT_SOURCE_DIR}/android)
    ...
  • また、メインアプリ `build.gradle ` には、フィーチャーAPIへの依存関係を設定する必要があります。dependencies ブロック内で、
    implementation 'androidx.core:core:1.13.1'

    を

    implementation("com.google.android.play:feature-delivery:2.1.0")
  • に置き換えてください

    。
  • フィーチャーモジュールが提供するライブラリへのアクセスを実装します。フィーチャーモジュールはメインアプリから利用可能な場合もあれば利用できない場合もあるため、モジュールはビルド時にリンクされず、モジュールへの呼び出しは実行時に解決される必要があります。例:
    QString MapLoader::loadMapInfo()
    {
        QScopedPointer<QString>resultStr;
        typedef void*(*LoadMapInfoFunc)();
        // wintermap ライブラリが存在するかどうかを確認
        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<QString*>(result));
        }else
            qWarning() << Q_FUNC_INFO << "Function loadMapInfo not loaded";
    
       return *resultStr.data();
    }
  • メインアプリのユーザーインターフェースやその他の必要な部分を実装します。
機能モジュール (Qt)
  • Qt Creator を使用してアプリを作成します(Qt C++ ライブラリプロジェクトテンプレートを使用しました)。
  • モジュールが提供する機能を実装します。
Android プロジェクト (Android)

Feature Delivery 向けの Android アプリバンドルをビルドするためのプロジェクトの作成は、主に以下の Android のドキュメントに基づいています:

Androidプロジェクトを手動で、またはAndroid Studioを使用して(「No Activity」テンプレートを使用)作成します。プロジェクトは、トップレベルのプロジェクトと、app およびfeature-module の2つのサブプロジェクトを含むように変更されます。Android Studioのテンプレートによりapp サブプロジェクトが作成され、feature-module はFile -> New -> New 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 に機能モジュールを追加し、必要に応じてrootProject.name を変更します:
    ...
    rootProject.name = "name-of-the-root-project"
    include(:app)
    include(:name-of-the-feature-module)
app - サブプロジェクト
  • Androidプロジェクトでは、メインアプリプロジェクトのQtバイナリが必要です:
    • Qtビルドのネイティブライブラリ([build directory]/android-build/libs/[target ABI] )をapp/src/main/jniLibs/[target ABI]
    • [build directory]/android-build/libs/ 内の JAR ファイルを以下の場所にコピーします:app/libs/
  • 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>
  • app/src/main/res/raw フォルダに、以下の内容を含むkeep.xml ファイルを追加します:
    <?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 - サブプロジェクト
build.gradle
  • buildScript およびrepositories ブロックを削除してください。
  • メインアプリの `build.gradle ` 内の `Android` ブロックには、いくつかの変更が必要です:
    • 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
...
}

また、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では、gradle.properties にプロジェクト変数が追加されました。必要に応じて、androidPackageName の値を変更してください。

AndroidManifest.xml
  • 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 ビルドから生成された機能モジュールのバイナリは、[name-of-feature-module]/src/main/jniLibs/
  • メインアプリと同様に、src/main/res/ フォルダには、xml フォルダとvalues フォルダがあり、それぞれにqtprovider_paths.xml とlibs.xml が含まれている必要があります。これらのファイルはアプリプロジェクトからコピーできます。
  • 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 は、アプリプロジェクトのものと非常に似ていますが、いくつかの変更点があります。以下に例を示します:
    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.properties ファイルはアプリサブプロジェクトからコピーできますが、androidPackageName をフィーチャーモジュールのパッケージ名に変更してください。
ビルドとデプロイ

AABバンドルは、Gradleラッパーを使用してコマンドラインからビルドできます:./gradlew bundle。生成されたAABは、build/outputs/bundle/release (またはdebug )フォルダ内に保存されます。その後、AABをGoogle Playストアにコピーして、テスト用にリリースできます。また、bundletool に--local-testing パラメータを指定することで、ローカルでテストを行うことも可能です。

サンプルプロジェクト @ 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.