gradle-experimentalプラグインを使ってNDKアプリケーションを作成します。
Android Studio 1.3ではNDKを正式にサポートしていません。
Table of Contents
1 Android Studio 1.3のNDKサポート状況
Android Studio 1.3はデフォルトでNDKをサポートしていません。gradle-experimentalプラグインを使う必要があります。Android Studio 1.3でNDKを使用しているサンプルコードはgradle-experimentalを使用しています。
gradle-experimentalプラグインは既存のAndroid.mkを流用できません。現状はAndroid.mkの内容をapp/build.gradleにDSLとして記述します。Android.mkを作成したプロジェクトは移植が必要となります。
Android.mkをそのまま使えるようになることを今後期待しましょう。MakefileをAndroid.mkに変換した工数がまた発生しないことを願って。
2 作成するアプリケーションの内容
Android Studioのデフォルトでプロジェクトを作成します。NDKのコードで__android_log_printを呼び出すJNI関数を定義します。JavaのコードでJNI関数を呼び出します。JNIの使い方はこちらを参照してください。
3 Android Studioでプロジェクトを作成
NDKApplicationという名前でプロジェクトを作成します。他の設定はデフォルトのままにします。
4 gradle-experimentalプラグインを使う
gradle-experimentalプラグインとは、Android Studioで使われているgradleプラグインの実験的なバージョンです。Android Studioで使われているgradleプラグインはGradle 2.4を使用しますが、gradle-experimentalプラグインはGradle 2.5を使用します。
4.1 build.gradle
build.gradleでgradle-experimentalプラグインを読み込むようにします。gradleとgradle-experimentalのバージョンは完全に別物です。
diff -uprN NDKApplication.org/build.gradle NDKApplication/build.gradle --- NDKApplication.org/build.gradle 2015-10-01 01:33:17.000000000 +0900 +++ NDKApplication/build.gradle 2015-10-01 01:41:14.000000000 +0900 @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.android.tools.build:gradle-experimental:0.2.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files
4.2 gradle/wrapper/gradle-wrapper.properties
gradle/wrapper/gradle-wrapper.propertiesでGradle 2.5を使用するようにします。
diff -uprN NDKApplication.org/gradle/wrapper/gradle-wrapper.properties NDKApplication/gradle/wrapper/gradle-wrapper.properties --- NDKApplication.org/gradle/wrapper/gradle-wrapper.properties 2015-10-01 02:02:18.000000000 +0900 +++ NDKApplication/gradle/wrapper/gradle-wrapper.properties 2015-10-01 01:36:03.000000000 +0900 @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
4.3 app/build.gradle
gradle-experimentalプラグインの変更に伴い、app/build.gradleの書式を変更します。
Android Studioでプロジェクトを作成した場合は以下のようになっています。
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { applicationId "com.hiroom2.ndkapplication" minSdkVersion 23 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
gradle-experimentalプラグインに対応させる為、以下のように変更します。modelというブロックが追加され、=で値が設定されるようになっています。
apply plugin: 'com.android.model.application' model { android { compileSdkVersion = 23 buildToolsVersion = "23.0.1" defaultConfig.with { applicationId = "com.hiroom2.ndkapplication" minSdkVersion.apiLevel = 23 targetSdkVersion.apiLevel = 23 versionCode = 1 versionName = "1.0" } buildTypes.with { release { minifyEnabled = false proguardFiles += file('proguard-rules.pro') } } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
対応が十分でない場合、以下のエラーが発生します。
Failed to sync Gradle project 'NDKApplication' Error:Unable to load class 'com.android.build.gradle.managed.ProductFlavor_Impl'. Possible causes for this unexpected error include: * Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.) Re-download dependencies and sync project (requires network) * The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem. Stop Gradle build processes (requires restart) * Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project. In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.
4.4 Java 1.7互換
Java 1.8を使っている場合、以下のエラーが発生します。
Error:com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000) at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472) at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406) at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388) at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251) at com.android.dx.command.dexer.Main.parseClass(Main.java:764) at com.android.dx.command.dexer.Main.access$1500(Main.java:85) at com.android.dx.command.dexer.Main$ClassParserTask.call(Main.java:1684) at com.android.dx.command.dexer.Main.processClass(Main.java:749) ... 19 more UNEXPECTED TOP-LEVEL EXCEPTION:
Java 1.7互換にする為、app/build.gradleに以下の設定を加えます。
diff -uprN NDKApplication.err/app/build.gradle NDKApplication/app/build.gradle --- NDKApplication.err/app/build.gradle 2015-10-01 02:17:56.000000000 +0900 +++ NDKApplication/app/build.gradle 2015-10-01 02:22:35.000000000 +0900 @@ -20,6 +20,11 @@ model { } } } + + compileOptions.with { + sourceCompatibility = JavaVersion.VERSION_1_7 + targetCompatibility = JavaVersion.VERSION_1_7 + } } dependencies {
5 NDK
NDKのコードを追加していきます。
5.1 JNI Folderの追加
projectのタブにてappを右クリックしてJNI Foloderを追加します。
JNI Folderの場所を設定します。デフォルトだとsrc/main/jniとなります。
5.2 NDKコードの追加
projectのタブにてjniを右クリックしてC/C++ Source Fileを追加します。
今回はhello.cという名前のCソースファイルをヘッダなしで作成します。
5.3 NDKコードの編集
hello.cを以下のように変更します。
// // Created by hiroom2 on 2015/10/01. // #include <jni.h> #include <android/log.h> JNIEXPORT void Java_com_hiroom2_ndkapplication_MainActivity_hello(JNIEnv *env, jobject obj) { __android_log_print(ANDROID_LOG_INFO, "JNI", "hello"); }
5.4 Javaコードの編集
libhello.soをロードして、onCreate実行時にJNIのhello関数を呼びます。
$ diff -uprN NDKApplication{.org,}/app/src/main/java/com/hiroom2/ndkapplication/MainActivity.java --- NDKApplication.org/app/src/main/java/com/hiroom2/ndkapplication/MainActivity.java 2015-10-01 05:21:51.000000000 +0900 +++ NDKApplication/app/src/main/java/com/hiroom2/ndkapplication/MainActivity.java 2015-10-01 12:16:14.000000000 +0900 @@ -7,10 +7,17 @@ import android.view.MenuItem; public class MainActivity extends Activity { + static { + System.loadLibrary("hello"); + } + + private native void hello(); + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + hello(); } @Override
5.5 NDKのビルド設定
app/build.gradleのandroid.ndkブロックでモジュール名とライブラリを設定します。__android_log_printは-llogを必要とします。
androidブロック外部でandroid.ndkを使う必要があります(anroidブロック内部でndk.withを使うとJNI Folderがビルドされないようです)。
Android.mk | app/build.gradle |
---|---|
LOCAL_MODULE | moduleName |
LOCAL_LDLIBS | ldLibs |
LOCAL_SRC_FILES | なし(JNI Folder配下のコード全て) |
$ diff -uprN NDKApplication.org/app/build.gradle NDKApplication/app/build.gradle --- NDKApplication.org/app/build.gradle 2015-10-01 11:50:47.000000000 +0900 +++ NDKApplication/app/build.gradle 2015-10-01 12:44:07.000000000 +0900 @@ -21,6 +21,11 @@ model { } } + android.ndk { + moduleName = "hello" + ldLibs += "log" + } + compileOption.with { sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7
6 実行例
NDKApplicationのアクティビティが起動し、logcatに__android_log_print関数のログが出力されます。
10-01 12:44:43.962 23837-23837/? I/JNI﹕ hello