始める前に
このガイドは、ネイティブ プログラミングと Android 開発に固有の概念に精通していることを前提としています。
はじめに
ここでは、NDK の仕組みの概要について説明します。Android NDK は、C / C++(ネイティブ コード)を Android アプリに埋め込むことができるツールです。特に以下のような場合、ネイティブ コードを Android アプリ内で使用できると非常に便利です。
- プラットフォーム間でアプリを移植する場合。
- 既存のライブラリを再利用する場合や、再利用向けに独自ライブラリを提供する場合。
- ゲームなど、特に演算負荷の高い処理を実行するために優れたパフォーマンスが必要となる場合。
仕組み
このセクションでは、最初に Android 向けネイティブ アプリのビルドで使用する主要コンポーネントについて説明し、次にビルドとパッケージ化のプロセスについて説明します。
主要コンポーネント
アプリをビルドする際は、次のコンポーネントについて理解しておく必要があります。
- ネイティブ共有ライブラリ: NDK はネイティブ共有ライブラリ(
.so
ファイル)を C / C++ ソースコードからビルドします。 - ネイティブ静的ライブラリ: NDK は静的ライブラリ(
.a
ファイル)をビルドすることもできます。静的ライブラリは別のライブラリにリンクすることができます。 - Java Native Interface(JNI): JNI は Java コンポーネントと C++ コンポーネントのインタラクションを可能にするインターフェースです。このガイドは、JNI の知識があることを前提にしています。詳細については、Java Native Interface の仕様をご覧ください。
- アプリケーション バイナリ インターフェース(ABI): ABI は、アプリのマシンコードとシステムとの実行時のやり取りを正確に定義したものです。NDK は、この定義に基づいて
.so
ファイルをビルドします。ABI ごとに対応アーキテクチャは異なり、NDK では、32 ビット ARM、AArch64、x86、x86-64 用の ABI がサポートされています。詳細については、Android ABI をご覧ください。 - マニフェスト: Java コンポーネントのないアプリを作成する場合は、マニフェスト内で
NativeActivity
クラスを宣言する必要があります。手順について詳しくは、native_activity.h インターフェースを使用するをご覧ください。
フロー
Android 向けネイティブ アプリ開発の一般的なフローは以下のとおりです。
- Java で実装する部分と、ネイティブ コードとして実装する部分を決定して、アプリを設計します。
注: Java を完全に排除するという選択肢もありますが、表示や UI の制御といったタスクを行う際は、Android Java フレームワークを利用した方が簡単な場合があります。
- 他の Android プロジェクトの場合と同じように、Android アプリ プロジェクトを作成します。
- ネイティブ コードのみのアプリを作成する場合は、
AndroidManifest.xml
内でNativeActivity
クラスを宣言します。詳細については、ネイティブ アクティビティとアプリをご覧ください。 - ネイティブ ライブラリ(名前、フラグ、リンク先ライブラリ)とコンパイル対象のソースファイルを記述した
Android.mk
ファイルを「JNI」ディレクトリ内に作成します。 - 必要に応じて、ターゲット ABI、ツールチェーン、リリースモード / デバッグモード、STL を設定する
Application.mk
ファイルを作成します。値を指定しなかった場合は、以下のデフォルト値がそれぞれ使用されます。- ABI: すべての ABI(サポートが終了したものを除く)
- ツールチェーン: Clang
- モード: リリース
- STL: system
- ネイティブ ソースを、プロジェクトの
jni
ディレクトリ内に配置します。 - ndk-build を使用して、ネイティブ ライブラリ(
.so
、.a
)をコンパイルします。 - Java コンポーネントをビルドして、
.dex
実行ファイルを生成します。 - アプリの実行に必要な
.so
や.dex
などのファイルを、すべて APK ファイルにパッケージ化します。
ネイティブ アクティビティとアプリ
Android SDK には、完全なネイティブ アクティビティを記述できる NativeActivity
ヘルパークラスが用意されています。NativeActivity
によって Android フレームワークとネイティブ コードの間のやりとりを処理できるため、サブクラス化やメソッドの呼び出しは不要です。アプリがネイティブであることを AndroidManifest.xml
ファイル内で宣言するだけで、ネイティブ アプリの作成を開始できます。
NativeActivity
を使用する Android アプリは、他のアプリからサンドボックス化された独自の仮想マシン上で稼働します。そのため、JNI 経由で Android フレームワーク API にアクセスすることもできます。ただし、センサーや、入力イベント、アセットなど、特定のケースに関しては、JNI 経由で呼び出すことなく使用できるネイティブ インターフェースが NDK に用意されています。このようなサポートの詳細については、ネイティブ API をご覧ください。
ネイティブ アクティビティを開発するかどうかにかかわらず、従来の Android ビルドツールを使用してプロジェクトを作成することをおすすめします。従来のツールでプロジェクトを作成することで、Android アプリを確実に正しい構造でビルドし、パッケージ化することができます。
Android NDK では、以下のいずれかの方法でネイティブ アクティビティを実装できます。
native_activity.h
ヘッダーは、NativeActivity
クラスのネイティブ バージョンを定義します。これには、ネイティブ アクティビティの作成に必要なコールバック インターフェースとデータ構造が含まれます。コールバックはアプリのメインスレッドで処理されるため、コールバック実装がメインスレッドをブロックしないようにする必要があります。メインスレッドがブロックされると、コールバックが戻るまでメインスレッドからの応答がなくなるため、ANR(アプリケーション応答なし)エラーが発生する可能性があります。android_native_app_glue.h
ファイルは、native_activity.h
インターフェースの上部にビルドされる静的ヘルパー ライブラリを定義します。これにより、イベントループ内のコールバックや入力イベントなどを処理する別のスレッドが生成されます。このようなイベントを別スレッドに移動することで、コールバックがメインスレッドをブロックする状況を回避できます。
また、<ndk_root>/sources/android/native_app_glue/android_native_app_glue.c
ソースも使用可能であるため、それにより実装を変更できます。
この静的ライブラリの使用方法については、ネイティブ アクティビティのサンプルアプリとそのドキュメントをご覧ください。また、<ndk_root>/sources/android/native_app_glue/android_native_app_glue.h
ファイル内のコメントもご覧ください。
native_activity.h インターフェースを使用する
native_activity.h
インターフェースを使用してネイティブ アクティビティを実装するには:
- プロジェクトのルート ディレクトリ内に
jni/
ディレクトリを作成します。このディレクトリに、ネイティブ コードのすべてが保存されます。 AndroidManifest.xml
ファイル内でネイティブ アクティビティを宣言します。- ネイティブ アクティビティ用のファイルを作成し、
ANativeActivity_onCreate
変数内で指定した関数を実装します。ネイティブ アクティビティが起動すると、アプリはこの関数を呼び出します。この関数は C / C++ のmain
に似ており、ANativeActivity
構造体へのポインタを受け取ります。この構造体には、記述する必要のある各種コールバック実装への関数ポインタが含まれます。ANativeActivity->callbacks
に、コールバック実装に対する適切なコールバック関数ポインタを設定します。 ANativeActivity->instance
フィールドに、使用する各データのインスタンスのアドレスを設定します。- 起動時にアクティビティが実行する他の処理をすべて実装します。
ANativeActivity->callbacks
で設定したコールバックの残りの部分を実装します。コールバックを呼び出すタイミングについては、アクティビティのライフサイクルを管理するをご覧ください。- アプリの残りの部分を開発します。
- プロジェクトの
jni/
ディレクトリ内にAndroid.mk file
を作成して、ビルドシステム用にネイティブ モジュールを記述します。詳細については、Android.mk をご覧ください。 Android.mk
ファイルを作成したら、ndk-build
コマンドを使用してネイティブ コードをコンパイルします。- 通常どおり、Android プロジェクトをビルドしてインストールします。ネイティブ コードが
jni/
ディレクトリにある場合、そのコードからビルドされた.so
ファイルが、ビルド スクリプトによって自動的に APK にパッケージ化されます。
アプリに Java コードは含まれていないため、android:hasCode
を false
に設定します。
<application android:label="@string/app_name" android:hasCode="false">
アクティビティ タグの android:name
属性を NativeActivity
に設定する必要があります。
<activity android:name="android.app.NativeActivity" android:label="@string/app_name">
注: NativeActivity
はサブクラス化できます。サブクラス化した場合は、NativeActivity
の代わりにそのサブクラスの名前を使用してください。
meta-data
タグの android:value
属性で、アプリへのエントリ ポイント(C / C++ の main
など)を含む共有ライブラリの名前を指定します。その際は、ライブラリ名の lib
プレフィックスと .so
サフィックスを省略します。
<meta-data android:name="android.app.lib_name" android:value="native-activity" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
$ cd <path>/<to>/<project> $ <ndk>/ndk-build
他のサンプルコード
NDK サンプルをダウンロードするには、NDK サンプルをご覧ください。