はじめに
Android StudioでNDKをビルドするためのツールとして、cmakeとndk-buildがある。
公式にはcmakeが推奨なため、ndk-buildを使った方法の説明が不十分なので、ここに記録を残す。
https://developer.android.com/studio/projects/add-native-code
externalNativeBuildブロックを使う方法
基本的には、この方法を使う。
フォルダ構造
├── app
│ ├── build.gradle
│ ├── src
│ │ ├─- main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ ├── jni
│ │ │ │ ├── Android.mk
│ │ │ │ ├── Application.mk
│ │ │ │ ├── *.c
│ │ │ │ ├── *.h
│ │ │ ├── res
├── build.gradle
├── settings.gradle
build.gradleの設定
build.gradle
android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 28
ndk {
// NDKビルドするABIを指定する
abiFilters 'x86', 'armeabi-v7a'
}
}
externalNativeBuild {
ndkBuild {
// Android.mkのファイルパス
path 'src/main/jni/Android.mk'
}
}
buildTypes {
debug {
externalNativeBuild {
ndkBuild {
// debugビルド時にのみ指定する、ndk-buildの引数。
arguments 'LOCAL_CFLAGS+=-SOME_FLAG1', 'LOCAL_CFLAGS+=-DSOME_FLAG2'
}
}
}
release {
externalNativeBuild {
ndkBuild {
// releaseビルド時にのみ指定する、ndk-buildの引数。
arguments 'LOCAL_CFLAGS+=-SOME_FLAG3', 'LOCAL_CFLAGS+=-DSOME_FLAG4'
}
}
}
- Application.mkの APP_PLATFORMとAPP_ABIは無視され、build.gradleのabiFilters、minSdkVersionから自動で設定される。
ndk-build用カスタムタスクを追加する方法
externalNativeBuildブロックを使う方法だと、Android.mkのAPP_PLATFORMは無視され、build.gradleのminSdkVersionから自動で設定されてしまう。そのため次のようなプロジェクトの場合に問題となる。
- minSdkVersionは9
- NDKで作るnative libraryではOpenGL ES (18以上で利用サポート)を使う
(当然、API levelが18以上時のみSystem.LoadLibrary()をする必要がある。)
このようなプロジェクトの場合、ndk-build用のカスタムタスクを作成して実行する。
フォルダ構造
├── app
│ ├── build.gradle
│ ├── src
│ │ ├─- main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ ├── native ## jniとするとerrorになる。
│ │ │ │ ├── Android.mk
│ │ │ │ ├── Application.mk
│ │ │ │ ├── *.c
│ │ │ │ ├── *.h
│ │ │ ├── res
├── build.gradle
├── settings.gradle
build.gradleの設定
build.gradle
android {
defaultConfig {
minSdkVersion 9
targetSdkVersion 28
}
// Put src/main/libs in aar
sourceSets {
main {
jniLibs.srcDir 'src/main/libs'
}
}
}
tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn buildNative}
// ndk-build用の buildNativeタスクの作成
task buildNative(type: Exec) {
// ndk-buildの設定。 環境変数ANDROID_NDK_HOMEで設定
def ndkBuild;
def ndkBuildingDir = file(new File("src/main"))
def hasNdk = false;
if (System.env.ANDROID_NDK_HOME != null) {
hasNdk = true;
ndkBuild = new File(System.env.ANDROID_NDK_HOME, 'ndk-build')
}
// ndk-buildの実行
commandLine ndkBuild, "--directory", ndkBuildingDir, "APP_BUILD_SCRIPT=${ndkBuildingDir}/native/Android.mk", "NDK_APPLICATION_MK=${ndkBuildingDir}/native/Application.mk", "-j", Runtime.runtime.availableProcessors()
doFirst {
if (!hasNdk) {
logger.error("ERROR: ANDROID_NDK_HOME not set.")
}
assert hasNdk : "ANDROID_NDK_HOME not set"
}
}
// ndk-build clean用の task cleanNativeの作成
clean.dependsOn 'cleanNative'
task cleanNative(type: Exec) {
def ndkBuild;
def ndkBuildingDir = file(new File("src/main"))
def hasNdk = false;
if (System.env.ANDROID_NDK_HOME != null) {
hasNdk = true;
ndkBuild = new File(System.env.ANDROID_NDK_HOME, 'ndk-build')
}
commandLine ndkBuild, "--directory", ndkBuildingDir, "APP_BUILD_SCRIPT=${ndkBuildingDir}/native/Android.mk", "NDK_APPLICATION_MK=${ndkBuildingDir}/native/Application.mk", "-j1", "clean"
doFirst {
if (!hasNdk) {
logger.error("ERROR: ANDROID_NDK_HOME not set.")
}
assert hasNdk : "ANDROID_NDK_HOME not set"
}
}