Android NDKについて、年末だしいろいろ棚卸しするよ

需要があるか判らないけど、自分のためにも書いておきます。

NDKとは?

Androidでの開発において、プロセッサネイティブなコードを使用して処理の高速化などを行う場合、
Androidが公式に提供しているNDK(Native Development Kit)を使用します。
2012年12月31日現在での最新バージョンはr8dというものになります。

ダウンロードはこちらから行えます

NDKで出来る事

  • CやC++でコードを書くことができます。
  • コンパイラとしてgcc-4.4.3, gcc-4.6, gcc-4.7, clang-3.1を利用できます。
  • プロセッサのアーキテクチャに依存した命令を書くことができます(NEONとか)。
  • JNIを通して、DalvikVMからコードを相互に呼び出すことができます。
  • armeabi, armeabi-v7a, x86, mips用それぞれのコードを生成することができます。
  • NativeActivityを使用してGUIアプリケーションを記述できます。
  • Linuxカーネルのシステムコールを利用したコードを記述できます(forkしたりできます)。
  • Androidの標準のものやサードパーティの既存のライブラリをStatic LibraryまたはShared Libraryとして利用できます。
  • DalvikVMのヒープサイズ制限の外側でメモリを使用できます。

NDKについて知っておくと、そのうち使える日が来るかもしれない知識

NDKのコマンドライン編

  • NDKによるビルドは、NDKパッケージ中に存在する「ndk-build」を使って行います。「ndk-build」にはGNU makeに渡すのと同じコマンドライン引数を指定できるので、並列ビルド用の「-j2」などを指定することができます。
  • buildが上手く行かない場合には「$ ndk-build -n」などとして、実際に使用されるコマンドライン文字列を出力させながら問題点を見つけることができます。
  • NDKのパッケージ中にはビルド済みの「gdbserver」が同梱されています。gdbserverを利用することで、ネイティブな部分に対してデバッガを利用した解析を行うことができます。gdbserverの操作方法についての解説は省きます。
  • まさか手作業で書いてる人はいないと思いますが、JNI用のヘッダファイルを自動生成するのには「javah」コマンドを利用できます。javahはJDKに入っているコマンドラインツールです。

ファイル構成編

  • NDKを利用する場合でも、Androidのプラットフォームのバージョンに依存する部分が存在します。NDKでの開発の際は、「default.properties」または「project.properties」に"target=android-XX"(XXにはAndroidAPIバージョンの数字が入ります)と記載することで、バージョンを設定します。
  • アプリケーション全体に渡る設定は「Application.mk」に記述します。「APP_ABI」や「APP_OPTIM」などが該当します。詳細については、NDKパッケージ中の「docs/APPLICATION-MK.html」に記載されています。
  • モジュールをビルドするためのMakeファイルの代わりになるのが「Android.mk」です。Androidのビルドシステムが用意しているマクロを利用してビルドを行う形になります。通常のMakefileと同様に「include」したり、シェルの機能を呼び出すこともできます。Static Library, Shared Library, Executable Binaryの3種類それぞれごとに、モジュールという形でビルド方法を記述していきます。詳細については、NDKパッケージ中の「docs/ANDROID-MK.html」に記載されています。

C編

  • C言語消えてくれるともっと幸せになれるのですが・・・。ちなみに「-std=c99」をつければC99が使えます。

C++

  • C++のランタイムライブラリには「system」という名前の標準のもの(例外やRTTIなどの機能が制限されています)と、「GAbi++」というもの、その他にSTL系のライブラリが存在します。
  • STLライブラリにはSTLport系の「stlport_static」、「stlport_shared」、GNU STLの「gnustl_static」, 「gnustl_shared」があります。詳細はNDKのインストールディレクトリ中の「docs/CPLUSPLUS-SUPPORT.html」に記載されています。それぞれ例外のサポートなどが異なるので注意が必要です。
  • 上記のライブラリの指定は、「Application.mk」の「APP_STL」の値として記述します。
  • 例外を使用する場合は、APP_STLに例外をサポートしているライブラリを選択した上で、「Application.mk」の「APP_CPPFLAGS」に"-fexceptions"を付け加えます(Android.mkの中で個別に指定しても構いません)。
  • RTTIを使用する場合は、APP_STLにRTTIをサポートしているライブラリを選択した上で、「Application.mk」の「APP_CPPFLAGS」に"-frtti"を付け加えます(Android.mkの中で個別に指定しても構いません)。
  • C++11を使用する場合は、「Application.mk」の「APP_CPPFLAGS」に"-std=c++0x"を付け加えます(Android.mkの中で個別に指定しても構いません)。GNU拡張版のC++11を使用する場合は"-std=gnu++0x"を指定してください。

JNI編

  • 一度に作成できるローカルリファレンスの最大数はせいぜい256個程度までのようです。
  • pthread_createしたスレッド内部でFindClassなどを使用すると、なぜかNoClassDefFoundになるので、pthread_createする前にFindClassなどをする必要があります。

その他

  • Androidが使用しているlibcは「bionic」というもので、Linuxのlibcとは別物になります。従って、LinuxではサポートされているのにAndroidではサポートされていない関数が存在します。
  • gcc4.7を使用する場合は、「Application.mk」の「NDK_TOOLCHAIN_VERSION」に"4.7"を指定します。
  • 大抵の環境では、メモリを食い過ぎるとネイティブプロセスであってもSIGKILLで殺されます。
  • プロセス間通信やプロセスの複製、デバイスドライバに対するioctlの発行など、いろんなことができます。
  • Android標準で提供されてるShared Libraryを、NDK環境でリンクして使いたい場合は、「Android.mk」の「LOCAL_LDFLAGS」などに上手いこと書くか、ダミーのモジュールを定義して、「LOCAL_SHARED_LIBRARIES」などに指定した上で、実機などから吸い出したライブラリを「obj/local/xxx」(xxxにはarmeabiなどが入ります)などに配置します。