Gradle をネイティブ ライブラリにリンクする

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

Android プロジェクト内の各モジュールがリンクできるのは、それぞれ 1 つのスクリプト ファイル(CMake または ndk-build)に限られます。そのため、たとえば複数の 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 ブロックを使って設定する必要があります。

Groovy

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"
    }
  }
}

Kotlin

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 = file("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 プロパティを使用して、特定のプロダクト フレーバーのアーティファクトのサブセットのみをビルドしてパッケージ化することが可能です。設定できるプロパティの一部を、次のサンプルコードに示します。

Groovy

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 app.
          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 app 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 {...}
  }
}

Kotlin

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 += listOf("-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang")

        // Sets a flag to enable format macro constants for the C compiler.
        cFlags += listOf("-D__STDC_FORMAT_MACROS")

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

  buildTypes {...}

  productFlavors {
    ...
    create("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 app.
          targets += listOf("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 app satisfies most project requirements.
                  "my-executible-demo")
        }
      }
    }

    create("paid") {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets += listOf("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 でパッケージ化する場合、それをモジュールの src/main/jniLibs/ABI ディレクトリに追加します。

バージョン 4.0 より前の Android Gradle プラグインでは、CMake の IMPORTED ターゲットをアプリに組み込むには、それを jniLibs ディレクトリに含める必要がありました。以前のバージョンのプラグインから移行する場合には、次のようなエラーが発生する可能性があります。

* What went wrong:
Execution failed for task ':app:mergeDebugNativeLibs'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > More than one file was found with OS independent path 'lib/x86/libprebuilt.so'

Android Gradle プラグイン 4.0 を使用している場合、CMake の IMPORTED ターゲットが使用するライブラリを jniLibs ディレクトリから移動することでこのエラーを回避できます。

ABI を指定する

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

Groovy

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 app.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

Kotlin

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 app.
      abiFilters += listOf("x86", "x86_64", "armeabi", "armeabi-v7a",
                   "arm64-v8a")
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

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

ダウンロード時に配信されるのは、ユーザーのデバイスの ABI に一致するネイティブ ライブラリだけであるため、アプリのサイズをさらに小さくするために、Android App Bundle を使用して公開することをおすすめします。

APK を使用して旧式アプリ(2021 年 8 月より前に作成)を公開する場合は、ABI に基づくマルチ APK を設定することをおすすめします。ネイティブ ライブラリのすべてのバージョンを組み込んだ単一の大きな APK を作成するのではなく、サポート対象の ABI ごとに Gradle で個別の APK を作成し、各 ABI に必要なファイルだけをパッケージ化するようにします。上記のサンプルコードに示すように、abiFilters フラグを指定せずに、ABI ごとに複数の APK を設定する場合、ネイティブ ライブラリについて、サポート対象のすべての ABI バージョンが Gradle でビルドされますが、パッケージ化されるのは、マルチ APK 設定で指定するものだけです。不要なバージョンのネイティブ ライブラリをビルド対象から除外するには、abiFilters フラグと ABI ごとのマルチ APK 設定の両方に、ABI の同じリストを指定します。