Android
NDK
AndroidStudio

Android StudioのNDK開発機能を使おう!

More than 3 years have passed since last update.

@eaglesakura です。

5月のGoogle IOでAndroid StudioのNDK対応が発表されてからずっとNDK対応するする詐欺を1ヶ月半されてきましたが、7/10のアップデート(Android Studio 1.3 RC1)でようやく待望のNDK対応アップデートが行われました。

使った感じはかなり良いのですが、前提条件やハマりどころが多いので、自分用のメモを兼ねて投稿します。


準備

準備を行わずにASのアップデートのみを行っても、NDK機能は全く動作してくれません。また、ビルドも行ってくれません。

それを回避するためには、前提条件をクリアする必要があります。


  1. Android Studio 1.3 RC1をインストールする

  2. JDK 1.7をインストールする


    • Android Studioの環境変数もちゃんとJDK1.7に変更しておきましょう。



  3. Gradle 2.5をインストールする


    • 最近GradleがC/C++対応したのはコレ関係かもしれないですね。



スクリーンショット 2015-07-11 9.29.21.png


新Pluginに合わせてbuild.gradleをかなり書き換える

NDK対応を行うためか次のステップのためかはわかりませんが、Android StudioのNDK機能を利用するためには、"実験用"とされてるPluginを利用する必要があります。

このPluginではbuild.gradleの記述がかなり変更されているため、実案件で使うと将来痛い目に会うかもしれません。また、確認した限りいくつかのバグがまだあります。

まず、experimental用のGradlePluginは今までとは別なPluginとして扱われるので、classpathを書き換える(もしくは追記する)必要があります。


root/build.gradle

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"ブロックが包んだりしているので、互換性とかありません。ガンバって書き換えましょう。


root/プロジェクト/build.gradle

// 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側と同じです。

スクリーンショット 2015-07-11 9.28.08.png


JNIデバッグをONにする

ドキュメントのどこにも見つかりませんでしたが、JNIのデバッグをONにするためには、android.buildTypesブロックの中でisJniDebuggable=trueをする必要があります。

次に"Edit Cofigures"でAndroid Nativeの実行設定を作成して、それに切り替えます。

スクリーンショット 2015-07-11 9.28.46.png

スクリーンショット 2015-07-11 9.26.11.png

あとはいつものようにデバッグボタンを押して実行すると、JNIのデバッグ接続が行われます。


起動直後は停止できない

NDK用のデバッガ(LLDB)は、アプリが起動後に接続される(しかも数秒かかる)ため、onCreate等の起動直後のブレークポイントはそのままでは捉えることが出来ません。接続されているかどうかをチェックするためには、Debugのログを観察しましょう。

"Debugger attached to process XXXX"と表示されたタイミングから、ブレークポイントで止めることが出来ます。

スクリーンショット 2015-07-11 9.36.03.png


起動直後のブレークポイントで停止させる

起動直後のブレークポイントで止めたい場合、少しだけ工夫が必要です。Androidの開発者機能で"デバッグアプリを選択"をして、デバッグしたいアプリを選択します。

device-2015-07-11-093830.png

次に"デバッガを待機"を有効化します。

device-2015-07-11-093931.png

これでデバッガが接続されるまで、アプリの起動を保留することが出来ます。この状態で再度実行すると、こんな感じで止まります。

device-2015-07-11-094059.png

ただしNDKのデバッガ(LLDB)が接続されても、AndroidはLLDBをデバッガと認識しないため、いつまでたっても起動しません。そこで、Java側のデバッガも追加でアタッチします。

メニューの"Attach debugger to Android process"を選択して、接続したいアプリのpackageを選択してOKします。


  • 画像の一番右側 スクリーンショット 2015-07-11 9.40.45.png

スクリーンショット 2015-07-11 9.43.09のコピー.png

するとAndroidはデバッガ接続を認識してアプリ起動を続行してくれるので、あとはブレークポイントを待てばOKです。Javaのデバッガも同時接続しているので動作は緩慢になりますが、この方法であればNDK/SDK両方のデバッグを行えます。便利ですね。


  • C++のブレークポイントで止められる


    • スクリーンショット 2015-07-11 9.45.14.png



  • 値を見ることが出来る


    • スクリーンショット 2015-07-11 9.45.18.png



  • 値の強制的な書き換えもできる(私の環境だと変数を指定してF2ボタン)


    • スクリーンショット 2015-07-11 9.46.45.png

    • スクリーンショット 2015-07-11 9.46.37.png




現行バージョンのGradleプロジェクトとの連携

実際の開発で使おうと思うと非常に問題となるのは、「そもそもbuild.gradleの構成が違いすぎる」ということです。例えば、android-aptのPluginは正常に動作しませんでした。

これはライブラリプロジェクトを併用することで回避できます。具体的には、Java側をライブラリプロジェクトに移行して、C++側だけを新しいプロジェクト構成にすればOKです。

現状のASはライブラリプロジェクトもデバッガを接続でき、なおかつ従来構成のgradleも今までどおり利用できます。併用を出来るようにしてくれたのは、非常にありがたい点でした。


現状での不明な機能


  • 指定した.cppや.cをビルドから除外する


    • ビルドはディレクトリ単位で突っ込まれるので、もしOSS等でtest用のcppがディレクトリに含まれていると、ビルドが壊れる原因になる。

    • glmとか。




現状の困った不具合


  • clangでビルドすると、成果物にapkが含まれない


困ったときのLink


最後に


  • 別な環境だと別な点でドはまりするかもしれません。

  • 何かあったら追記していきます。