Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

ネイティブ ライブラリへの Gradle のリンク

ネイティブ ライブラリ プロジェクトを Gradle ビルドの依存関係として含めるには、CMake または ndk-build のスクリプト ファイルへのパスを Gradle に指定する必要があります。アプリをビルドすると、Gradle は CMake または ndk-build を実行し、APK と共有ライブラリをパッケージ化します。さらに、ビルド スクリプトも使って、Android Studio プロジェクトに含めるファイルも判別するので、[Project] ウィンドウからそれらのファイルにアクセスできます。ネイティブ ソースのビルド スクリプトがない場合は、次の手順に進む前に CMake ビルド スクリプトを作成する必要があります。

Android プロジェクトのモジュールはそれぞれ CMake または ndk-build のスクリプト ファイルのどちらか 1 つにのみリンクできます。そのため、たとえば、複数の CMake プロジェクトからビルドして出力をパッケージ化する場合、1 つの CMakeLists.txt ファイルを最上位の CMake ビルド スクリプトとして使用し(Gradle をそれにリンクし)、そのビルド スクリプトの依存関係として、その他の CMake プロジェクトを追加する必要があります。ndk-build を使用する場合も同様に、最上位の Android.mk スクリプト ファイルにその他の makefile を含めることができます。

Gradle をネイティブ プロジェクトにリンクすると、Android Studio では [Project] ペインが更新され [cpp] グループにソース ファイルとネイティブ ライブラリが表示され、[External Build Files] グループに外部ビルド スクリプトが表示されます。

注: Gradle の設定を変更したときは、必ずツールバーのプロジェクトの同期アイコン をクリックして変更を適用してください。また、Gradle にリンクした後に CMake または ndk-build のスクリプト ファイルを変更したときは、メニューバーから [Build] > [Refresh Linked C++ Projects] を選択することで、Android Studio と変更内容を同期する必要があります。

Android Studio UI を使用して Gradle を外部の CMake または ndk-build のビルド プロジェクトにリンクすることができます。

  1. IDE の左側の [Project] ペインを開き、[Android] ビューを選択します。
  2. app モジュールなど、ネイティブ ライブラリにリンクするモジュールを右クリックし、メニューから [Link C++ Project with Gradle] を選択します。図 4 に示すようなダイアログが表示されます。
  3. プルダウン メニューから [CMake] または [ndk-build] を選択します。
    1. [CMake] を選択した場合、[Project Path] の横のフィールドで、外部 CMake プロジェクトの CMakeLists.txt スクリプト ファイルを指定します。
    2. [ndk-build] を選択した場合、[Project Path] の横のフィールドで、外部 ndk-build プロジェクトの Android.mk スクリプト ファイルを指定します。Android Studio は、Android.mk ファイルと同じディレクトリに配置されている Application.mk ファイルがあれば、それも対象に含めます。

    図 4. Android Studio ダイアログを使って外部の C++ プロジェクトをリンクする

  4. [OK] をクリックします。

Gradle を手動で設定する

Gradle をネイティブ ライブラリにリンクするように手動で設定するには、externalNativeBuild ブロックをモジュール レベルの build.gradle ファイルに追加し、cmake ブロックまたは ndkBuild ブロックを使って設定する必要があります。

    android {
      ...
      defaultConfig {...}
      buildTypes {...}

      // Encapsulates your external native build configurations.
      externalNativeBuild {

        // Encapsulates your CMake build configurations.
        cmake {

          // Provides a relative path to your CMake build script.
          path "CMakeLists.txt"
        }
      }
    }
    

注: Gradle を既存の ndk-build プロジェクトにリンクする場合は、cmake ブロックではなく ndkBuild ブロックを使用して、Android.mk ファイルへの相対パスを指定します。Gradle は、Android.mk ファイルと同じディレクトリに配置されている Application.mk ファイルがあれば、それも対象に含めます。

オプションの設定を指定する

CMake または ndk-build のオプションの引数やフラグを指定するには、モジュール レベルの build.gradle ファイルの defaultConfig ブロック内に別の externalNativeBuild ブロックを設定します。defaultConfig ブロック内のその他のプロパティと同様に、ビルド設定のプロダクト フレーバーごとに、これらのプロパティをオーバーライドできます。

たとえば、CMake または ndk-build のプロジェクトで複数のネイティブ ライブラリや実行可能ファイルを定義する場合、targets プロパティを使用して、特定のプロダクト フレーバーのアーティファクトのサブセットのみをビルドしてパッケージ化することができます。設定できるプロパティの一部を、次のサンプルコードに示します。

    android {
      ...
      defaultConfig {
        ...
        // This block is different from the one you use to link Gradle
        // to your CMake or ndk-build script.
        externalNativeBuild {

          // For ndk-build, instead use the ndkBuild block.
          cmake {

            // Passes optional arguments to CMake.
            arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

            // Sets a flag to enable format macro constants for the C compiler.
            cFlags "-D__STDC_FORMAT_MACROS"

            // Sets optional flags for the C++ compiler.
            cppFlags "-fexceptions", "-frtti"
          }
        }
      }

      buildTypes {...}

      productFlavors {
        ...
        demo {
          ...
          externalNativeBuild {
            cmake {
              ...
              // Specifies which native libraries or executables to build and package
              // for this product flavor. The following tells Gradle to build only the
              // "native-lib-demo" and "my-executible-demo" outputs from the linked
              // CMake project. If you don't configure this property, Gradle builds all
              // executables and shared object libraries that you define in your CMake
              // (or ndk-build) project. However, by default, Gradle packages only the
              // shared libraries in your APK.
              targets "native-lib-demo",
                      // You need to specify this executable and its sources in your CMakeLists.txt
                      // using the add_executable() command. However, building executables from your
                      // native sources is optional, and building native libraries to package into
                      // your APK satisfies most project requirements.
                      "my-executible-demo"
            }
          }
        }

        paid {
          ...
          externalNativeBuild {
            cmake {
              ...
              targets "native-lib-paid",
                      "my-executible-paid"
            }
          }
        }
      }

      // Use this block to link Gradle to your CMake or ndk-build script.
      externalNativeBuild {
        cmake {...}
        // or ndkBuild {...}
      }
    }
    

プロダクト フレーバーとビルド バリアントの設定について詳しくは、ビルド バリアントの設定に関する記事をご覧ください。arguments プロパティを使用して CMake で設定できる変数のリストについては、CMake 変数を使用する場合の説明をご覧ください。

ビルド済みのネイティブ ライブラリを含める

Gradle でビルド済みのネイティブ ライブラリと APK をパッケージ化する場合、デフォルトのソースセットの設定を変更して、下記に示すように、ビルド済みの .so ファイルのディレクトリを含めます。この操作は、Gradle にリンクする CMake のビルド スクリプトのアーティファクトを含める場合は不要です。

    android {
        ...
        sourceSets {
            main {
                jniLibs.srcDirs 'imported-lib/src/', 'more-imported-libs/src/'
            }
        }
    }
    

ABI を指定する

Gradle はデフォルトでは、NDK がサポートするアプリケーション バイナリ インターフェース(ABI)の個々の .so ファイルにネイティブ ライブラリをビルドし、それをすべて APK にパッケージ化します。Gradle を使ってネイティブ ライブラリの特定の API 設定のみビルドしてパッケージ化したい場合は、下記に示すように、モジュール レベルの build.gradle ファイル内で ndk.abiFilters フラグを使って指定できます。

    android {
      ...
      defaultConfig {
        ...
        externalNativeBuild {
          cmake {...}
          // or ndkBuild {...}
        }

        // Similar to other properties in the defaultConfig block,
        // you can configure the ndk block for each product flavor
        // in your build configuration.
        ndk {
          // Specifies the ABI configurations of your native
          // libraries Gradle should build and package with your APK.
          abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                       'arm64-v8a'
        }
      }
      buildTypes {...}
      externalNativeBuild {...}
    }
    

ほとんどの場合、必要なのは上記のように ndk ブロックに abiFilters を指定することだけです。この設定によって Gradle はネイティブ ライブラリの目的のバージョンを対象に、ビルドとパッケージ化の両方を行うからです。ただし、APK にパッケージ化する対象とは別個に、Gradle がビルドする対象を制御したい場合は、defaultConfig.externalNativeBuild.cmake ブロック(または defaultConfig.externalNativeBuild.ndkBuild ブロック)内で別の abiFilters フラグを設定します。Gradle はその ABI 設定をビルドしますが、パッケージ化するのは defaultConfig.ndk ブロックに指定されたものだけです。

APK のサイズをさらに削減するには、ABI に応じた複数の APK を設定することを検討します。ネイティブ ライブラリのすべてのバージョンを含めて 1 つの大きな APK を作成するのではなく、サポートする ABI ごとに、Gradle が個別の APK を作成し、各 ABI に必要なファイルのみパッケージ化します。上記のサンプルコードに示すように、abiFilters フラグを指定せずに、ABI ごとに複数の APK を設定する場合、ネイティブ ライブラリについて、サポート対象のすべての ABI バージョンが Gradle でビルドされますが、パッケージ化されるのは、マルチ APK 設定で指定するものだけです。不要なバージョンのネイティブ ライブラリをビルド対象から除外するには、abiFilters フラグと ABI ごとのマルチ APK 設定の両方に、ABI の同じリストを指定します。