コンセプト

前提知識

このガイドは、ネイティブ プログラミングと 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 を完全に排除するという選択肢もありますが、Android Java フレームワークを利用した方が表示や UI の制御などの処理がしやすくなります。

  2. 他の Android プロジェクトと同様に Android アプリ プロジェクトを作成します。
  3. ネイティブ コードのみでアプリを作成している場合は、AndroidManifest.xmlNativeActivity クラスを宣言してください。詳細については、ネイティブ アクティビティとアプリケーションをご覧ください。
  4. 「JNI」ディレクトリに、ネイティブ ライブラリ(名前、フラグ、リンク先ライブラリを含む)を記述する Android.mk ファイルと、コンパイル対象のソースファイルを作成します。
  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 にアクセスすることも可能です。ただし、センサー、入力イベント、アセットなどの特定のケースについては、NDK は JNI を介して呼び出さなくても使用できるネイティブ インターフェースを提供しています。これらのサポートの詳細については、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 サンプルをご覧ください。