概念

前提知識

このガイドは、ネイティブ プログラミングと 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 がサポートされています。詳細については、ABI 管理をご覧ください。
  • マニフェスト: Java コンポーネントのないアプリを作成している場合は、マニフェスト内で NativeActivity クラスを宣言する必要があります。詳細については、ネイティブ アクティビティとアプリの「native_activity.h インターフェースを使用する」をご覧ください。

フロー

Android 向けネイティブ アプリ開発の一般的なフローは以下のとおりです。

  1. Java で実装する部分と、ネイティブ コードとして実装する部分を決定して、アプリを設計します。

    注: Java を完全に排除するという選択肢もありますが、表示や UI の制御といったタスクを行う際は、Android Java フレームワークを利用した方が簡単な場合があります。

  2. 他の Android プロジェクトの場合と同じように、Android アプリ プロジェクトを作成します。
  3. ネイティブ コードのみのアプリを作成している場合は、AndroidManifest.xml 内で NativeActivity クラスを宣言します。詳細については、ネイティブ アクティビティとアプリをご覧ください。
  4. ネイティブ ライブラリ(名前、フラグ、リンク先ライブラリ)とコンパイル対象のソースファイルを記述した Android.mk ファイルを「JNI」ディレクトリ内に作成します。
  5. 必要に応じて、ターゲット ABI、ツールチェーン、リリースモード / デバッグモード、STL を設定する Application.mk ファイルを作成します。値を指定しない場合は、次のデフォルト値がそれぞれ使用されます。
    • ABI: すべての ABI(サポートが終了したものを除く)
    • ツールチェーン: Clang
    • モード: リリース
    • STL: system
  6. ネイティブ ソースを、プロジェクトの jni ディレクトリ内に配置します。
  7. ndk-build を使用して、ネイティブ ライブラリ(.so.a)をコンパイルします。
  8. Java コンポーネントをビルドして、.dex 実行ファイルを生成します。
  9. アプリの実行に必要な .so.dex などのファイルを、すべて APK ファイルにパッケージ化します。

ネイティブ アクティビティとアプリ

Android SDK には、完全なネイティブ アクティビティを記述できる NativeActivity ヘルパークラスが用意されています。NativeActivity によって Android フレームワークとネイティブ コード間のやりとりを処理できるため、サブクラス化やメソッドの呼び出しは不要です。アプリがネイティブであることを AndroidManifest.xml ファイル内で宣言するだけで、ネイティブ アプリの作成を開始できます。

NativeActivity を使用する Android アプリは、他のアプリからサンドボックス化された独自の仮想マシン上で稼働します。そのため、JNI 経由で Android フレームワーク API にアクセスすることもできます。ただし、センサーや、入力イベント、アセットなど、特定のケースに関しては、JNI 経由で呼び出すことなく使用できるネイティブ インターフェースが NDK に用意されています。サポートの詳細については、Android 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 インターフェースを使用してネイティブ アクティビティを実装するには:

  1. プロジェクトのルート ディレクトリ内に jni/ ディレクトリを作成します。このディレクトリに、ネイティブ コードのすべてが保存されます。
  2. AndroidManifest.xml ファイル内でネイティブ アクティビティを宣言します。
  3. アプリに Java コードは含まれていないため、android:hasCodefalse に設定します。

        <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>
        
  4. ネイティブ アクティビティ用のファイルを作成し、ANativeActivity_onCreate 変数内で指定した関数を実装します。ネイティブ アクティビティが起動すると、アプリはこの関数を呼び出します。この関数は C / C++ の main に似ており、ANativeActivity 構造体へのポインタを受け取ります。この構造体には、記述する必要のある各種コールバック実装への関数ポインタが含まれます。ANativeActivity->callbacks に、コールバック実装に対する適切なコールバック関数ポインタを設定します。
  5. ANativeActivity->instance フィールドに、使用する各データのインスタンスのアドレスを設定します。
  6. 起動時にアクティビティが実行する他の処理をすべて実装します。
  7. ANativeActivity->callbacks で設定したコールバックの残りの部分を実装します。コールバックを呼び出すタイミングについては、アクティビティのライフサイクルを管理するをご覧ください。
  8. アプリの残りの部分を開発します。
  9. プロジェクトの jni/ ディレクトリ内に Android.mk file を作成して、ビルドシステム用にネイティブ モジュールを記述します。詳細については、Android.mk をご覧ください。
  10. Android.mk ファイルを作成したら、ndk-build コマンドを使用してネイティブ コードをコンパイルします。
  11.     $ cd <path>/<to>/<project>
        $ <ndk>/ndk-build
        
  12. 通常どおり、Android プロジェクトをビルドしてインストールします。ネイティブ コードが jni/ ディレクトリにある場合、そのコードからビルドされた .so ファイルが、ビルド スクリプトによって自動的に APK にパッケージ化されます。

他のサンプルコード

NDK サンプルをダウンロードするには、NDK サンプルをご覧ください。