コンセプト

始める前に

このガイドは、ネイティブ プログラミングと 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 向けネイティブ アプリ開発の一般的なフローは以下のとおりです。

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

  2. 他の Android プロジェクトの場合と同じように、Android アプリ プロジェクトを作成します。

  3. ネイティブ コードのみのアプリを作成する場合は、AndroidManifest.xml 内で NativeActivity クラスを宣言します。詳細については、ネイティブ アクティビティとアプリをご覧ください。

  4. ネイティブ ライブラリ(名前、フラグ、リンク先ライブラリ)とコンパイル対象のソースファイルを記述した Android.mk ファイルを「JNI」ディレクトリ内に作成します。

  5. 必要に応じて、ターゲット ABI、ツールチェーン、リリースモード / デバッグモード、STL を設定する Application.mk ファイルを作成します。値を指定しなかった場合は、以下のデフォルト値がそれぞれ使用されます。

    • ABI: すべての ABI(サポートが終了したものを除く)
    • モード: リリース
    • 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 に用意されています。このようなサポートの詳細については、ネイティブ 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 ファイル内でネイティブ アクティビティを宣言します。

    アプリに 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">
    

    meta-data タグの android:value 属性で、アプリへのエントリ ポイント(C / C++ の main など)を含む共有ライブラリの名前を指定します。その際は、ライブラリ名の lib プレフィックスと .so サフィックスを省略します。

    <manifest>
      <application>
        <activity>
          <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>
    
  3. ネイティブ アクティビティ用のファイルを作成し、ANativeActivity_onCreate 変数内で指定した関数を実装します。ネイティブ アクティビティが起動すると、アプリはこの関数を呼び出します。この関数は C / C++ の main に似ており、ANativeActivity 構造体へのポインタを受け取ります。この構造体には、記述する必要のある各種コールバック実装への関数ポインタが含まれます。ANativeActivity->callbacks に、コールバック実装に対する適切なコールバック関数ポインタを設定します。

  4. ANativeActivity->instance フィールドに、使用する各データのインスタンスのアドレスを設定します。

  5. 起動時にアクティビティが実行する他の処理をすべて実装します。

  6. ANativeActivity->callbacks で設定したコールバックの残りの部分を実装します。コールバックを呼び出すタイミングについては、アクティビティのライフサイクルを管理するをご覧ください。

  7. アプリの残りの部分を開発します。

  8. プロジェクトの jni/ ディレクトリ内に Android.mk file を作成して、ビルドシステム用にネイティブ モジュールを記述します。詳細については、Android.mk をご覧ください。

  9. Android.mk ファイルを作成したら、ndk-build コマンドを使用してネイティブ コードをコンパイルします。

    cd <path>/<to>/<project>
    $NDK/ndk-build
    
  10. 通常どおり、Android プロジェクトをビルドしてインストールします。ネイティブ コードが jni/ ディレクトリにある場合、そのコードからビルドされた .so ファイルが、ビルド スクリプトによって自動的に APK にパッケージ化されます。

他のサンプルコード

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