@eaglesakura です。
5月のGoogle IOでAndroid StudioのNDK対応が発表されてからずっとNDK対応するする詐欺を1ヶ月半されてきましたが、7/10のアップデート(Android Studio 1.3 RC1)でようやく待望のNDK対応アップデートが行われました。
使った感じはかなり良いのですが、前提条件やハマりどころが多いので、自分用のメモを兼ねて投稿します。
準備
準備を行わずにASのアップデートのみを行っても、NDK機能は全く動作してくれません。また、ビルドも行ってくれません。
- そのあたりのツイート
- http://twitter.com/eaglesakura/status/619305979318939648
- http://twitter.com/eaglesakura/status/619306946579992576
- http://twitter.com/eaglesakura/status/619307831049596928
それを回避するためには、前提条件をクリアする必要があります。
- Android Studio 1.3 RC1をインストールする
- JDK 1.7をインストールする
- Android Studioの環境変数もちゃんとJDK1.7に変更しておきましょう。
- Gradle 2.5をインストールする
- 最近GradleがC/C++対応したのはコレ関係かもしれないですね。
新Pluginに合わせてbuild.gradleをかなり書き換える
NDK対応を行うためか次のステップのためかはわかりませんが、Android StudioのNDK機能を利用するためには、"実験用"とされてるPluginを利用する必要があります。
このPluginではbuild.gradleの記述がかなり変更されているため、実案件で使うと将来痛い目に会うかもしれません。また、確認した限りいくつかのバグがまだあります。
まず、experimental用のGradlePluginは今までとは別なPluginとして扱われるので、classpathを書き換える(もしくは追記する)必要があります。
buildscript {
repositories {
jcenter()
}
dependencies {
// classpathが変わった
classpath 'com.android.tools.build:gradle-experimental:0.1.+'
}
}
allprojects {
repositories {
jcenter()
}
}
さらに、プロジェクトをビルドするためのbuild.gradleが大胆に変更されています。Plugin名も変わっています。
記述方法は、従来は"compileSdkVersion 22"のように記述していた部分が、"compileSdkVersion = 22"のように"="で記述するようになりました。実装的には、メソッドではなくて変数に書き換わったかと思います。
- 全ての変数がそうなのか?というとそうではなくて、一部は従来のようにメソッドのままなので、多少統一感がありません。今後変更される可能性があります。
他にも"defaultConfig"ブロックが"defaultConfig.with"のように書き換えが必要だったり、結構変更は多いです。
そもそもandroidブロック全体を"model"ブロックが包んだりしているので、互換性とかありません。ガンバって書き換えましょう。
// Plugin名が変わった
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 22
buildToolsVersion = "23.0.0 rc3"
defaultConfig.with {
applicationId = "com.example.eaglesakura.ndk.helllo_ndk"
minSdkVersion.apiLevel = 15
targetSdkVersion.apiLevel = 22
versionCode = 1
versionName = "1.0"
}
// package除外は多少法則が乱れる。ややこしい。
packagingOptions.with {
exclude 'LICENSE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE'
exclude 'androidannotations-api.properties'
exclude 'services/com.fasterxml.jackson.core.JsonFactory'
exclude 'services/com.fasterxml.jackson.core.ObjectCodec'
}
}
android.buildTypes {
debug {
isJniDebuggable = true
}
release {
isMinifyEnabled = false
proguardFiles += file('proguard-rules.pro')
}
}
android.sources {
main {
jni {
source {
// JNIのソースディレクトリを変更したい場合、コレで追加削除できる
srcDirs = [file("src/main/jni"), file("ndk")]
}
}
}
}
android.ndk {
moduleName = "app"
}
android.productFlavors {
// 特定のabiだけビルドしたい
create("arm7") {
ndk.abiFilters += "armeabi-v7a"
}
// 全部のabiをビルドしたい
create("fat") {
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.1'
}
これでAndroid Studioが新Pluginを利用してくれて、NDK機能が有効化されるはずです。あとはjniのディレクトリのhoge.cppを入れておけば、勝手にビルドしてくれます。
NDK機能はJavaの"native"メソッドをチェックして、もしCPP側に実装されていなかったら警告を出すようにしてくれるので、非常に親切です。
ブレークポイントで止める
無事にビルドできたら、次はブレークポイントで止めてみたくなりますね。ブレークポイントの設定はJava側と同じです。
JNIデバッグをONにする
ドキュメントのどこにも見つかりませんでしたが、JNIのデバッグをONにするためには、android.buildTypesブロックの中でisJniDebuggable=trueをする必要があります。
- ドキュメントのどこにも書いてなかったので、1時間くらいドハマりしてました。
- http://twitter.com/eaglesakura/status/619340321537703937
次に"Edit Cofigures"でAndroid Nativeの実行設定を作成して、それに切り替えます。
あとはいつものようにデバッグボタンを押して実行すると、JNIのデバッグ接続が行われます。
起動直後は停止できない
NDK用のデバッガ(LLDB)は、アプリが起動後に接続される(しかも数秒かかる)ため、onCreate等の起動直後のブレークポイントはそのままでは捉えることが出来ません。接続されているかどうかをチェックするためには、Debugのログを観察しましょう。
"Debugger attached to process XXXX"と表示されたタイミングから、ブレークポイントで止めることが出来ます。
起動直後のブレークポイントで停止させる
起動直後のブレークポイントで止めたい場合、少しだけ工夫が必要です。Androidの開発者機能で"デバッグアプリを選択"をして、デバッグしたいアプリを選択します。
次に"デバッガを待機"を有効化します。
これでデバッガが接続されるまで、アプリの起動を保留することが出来ます。この状態で再度実行すると、こんな感じで止まります。
ただしNDKのデバッガ(LLDB)が接続されても、AndroidはLLDBをデバッガと認識しないため、いつまでたっても起動しません。そこで、Java側のデバッガも追加でアタッチします。
メニューの"Attach debugger to Android process"を選択して、接続したいアプリのpackageを選択してOKします。
するとAndroidはデバッガ接続を認識してアプリ起動を続行してくれるので、あとはブレークポイントを待てばOKです。Javaのデバッガも同時接続しているので動作は緩慢になりますが、この方法であればNDK/SDK両方のデバッグを行えます。便利ですね。
現行バージョンのGradleプロジェクトとの連携
実際の開発で使おうと思うと非常に問題となるのは、「そもそもbuild.gradleの構成が違いすぎる」ということです。例えば、android-aptのPluginは正常に動作しませんでした。
これはライブラリプロジェクトを併用することで回避できます。具体的には、Java側をライブラリプロジェクトに移行して、C++側だけを新しいプロジェクト構成にすればOKです。
現状のASはライブラリプロジェクトもデバッガを接続でき、なおかつ従来構成のgradleも今までどおり利用できます。併用を出来るようにしてくれたのは、非常にありがたい点でした。
現状での不明な機能
- 指定した*.cppや*.cをビルドから除外する
- ビルドはディレクトリ単位で突っ込まれるので、もしOSS等でtest用のcppがディレクトリに含まれていると、ビルドが壊れる原因になる。
- glmとか。
現状の困った不具合
- clangでビルドすると、成果物にapkが含まれない
困ったときのLink
- Google公式NDKサンプル
- https://github.com/googlesamples/android-ndk
- includeディレクトリの追加方法とかもここを参考にすればOK
- NDK Previewの解説ページ
- http://tools.android.com/tech-docs/android-ndk-preview
- Experimental Pluginの解説ページ
- http://tools.android.com/tech-docs/new-build-system/gradle-experimental
最後に
- 別な環境だと別な点でドはまりするかもしれません。
- 何かあったら追記していきます。