ものがたり(旧)

atsushieno.hatenablog.com に続く

android NDK 概要 (日本語訳)

ちょっとばかり趣向を変えて、Android NDK r4のOVERVIEW.TXTを翻訳したものを公開しておきます。やっつけ仕事だけど。訳文は http://gist.github.com/607476 にも置いてあります。てか最初はgithubにしか置かないつもりだったんだけど、読みにくいのでこちらで公開。

r4で根本的に変わったみたいですが、意外と日本語訳は見あたらなかったりして。NDKなんて普通のandroid開発者はあんまし使わないか。って僕もまだ使ったこと無いんですけどね。

フォーマットが合わないので多少みにくい加工をほどこしたのですが、そこはご愛敬。

Android NDK概要

イントロダクション:

Android NDKは、Androidアプリケーション開発者が、CやC++のソースファイルからコンパイルされたネイティブマシンコードを、アプリケーションパッケージに組み込めるようにするツールの集合です。

重要:
Android NDKは、Cupcate (すなわち1.5) 以降のプラットフォームを実行するAndroidシステムイメージのみを対象として使用できます。

特に、1.0および1.1のシステムイメージは、1.5リリースの際に生じた微妙なABIとツールチェインの変更によって、サポートされなくなりました。

I. Android NDKの目標:
--------------------

Android VMは、あなたのアプリケーションのソースコードで、ネイティブコードで実装されたメソッドを、JNIを通じて呼び出せるようにします。分かりやすく言うと、これはつまり:


- あなたのアプリケーションのソースコードでは、メソッドに'native'キーワードを使用して、ネイティブコード上で実装されているということを示して宣言します。例えば:

native byte[] loadFile(String filePath);

- あなたは、これらのメソッドの実装を含む共有ライブラリを、アプリケーションの .apkファイル中にパッケージして提供しなければなりません。このライブラリは、標準的なUnixの慣習に従って lib.so として命名されなければならず、標準的なJNIエントリポイントを含んでいるべきです(詳細は後述します)。例えば:

libFileLoader.so

- あなたのアプリケーションは、明示的にライブラリをロードしなければなりません。たとえば、アプリケーションの起動時にこれをロードする場合は、単純にソースコードに以下の行を追加します:

static {
System.loadLibrary ("FileLoader");
}

ここで、 'lib' プレフィックスと '.so' サフィックスを使用しないことに注意して下さい。

Android NDKは、Android SDKを補完して、以下の点であなたを助けるものです:


- ARM CPU上のAndroid 1.5(以降)のプラットフォームで実行できるJNI互換の共有ライブラリを生成する。

- 生成された共有ライブラリをアプリケーションのプロジェクト パスの適切な場所にコピーして、最終的な(そして署名された).apkに自動的に追加されるようにする。

- NDKの今後のリビジョンでは、リモートのgdb接続を通じて、あなたのネイティブコードをデバッグできるようなツールと、可能な限り多くのソース/シンボル情報を提供するつもりです。

さらに、NDKでは以下も提供しています:

- ネイティブARMバイナリをLinux, OS X, WindowsCygwin)上で生成できるクロス ツールチェイン(コンパイラ、リンカーなど)の集合

- Androidプラットフォームでサポートされている安定版のネイティブAPIのリストに対応するシステム ヘッダの集合。これは今後のプラットフォームのリリースの全てでサポートが保証されている定義群に一致します。

これらは docs/STABLE-APIS.TXT でドキュメントされています。

重要:
Androidシステムイメージのネイティブ システムライブラリの大部分は、APIが固まっておらず、今後のプラットフォームのアップデートおよびリリースで、根本的に変更または削除される可能性があります。

- 開発者が、どのソースをどのようにコンパイルするかを、非常に短いビルドファイルのみで記述できるようなビルドシステム。このビルドシステムは、面倒なツールチェイン/プラットフォーム/CPU/ABI固有の問題を全て扱います。さらに、今後のNDKのアップデートでは、さらなるツールチェイン、プラットフォーム、システムインターフェースを、開発者のビルドファイルへの変更を要求することなくサポート出来るようにします(詳細は後述します)。


II. Android NDKの目標でないもの:
-------------------------------

NDKは、Androidバイス上で実行できる汎用的なネイティブコードを書く良い方法*ではありません*。特に、あなたのアプリケーションはJavaプログラミング言語で書かれ、Androidシステムイベントを適切に処理して「アプリケーションが応答しません」のダイアログを回避したり、Androidアプリケーションのライフサイクルを処理するべきです。

ただし、アプリケーションの起動および停止を適宜処理する、小さな「アプリケーション ラッパー」を使って、ネイティブコードで複雑なアプリケーションを作成することは可能です。

JNIの環境では、多くの操作において、典型的なネイティブコードとは必ずしも共通しない独自のアクションが、開発者に要求されるため、JNIの十分な理解が大きく望まれます。たとえば:


- ネイティブポインタの直接参照によってVMオブジェクトの内容に直接アクセス出来ないようにすること。例えば、Stringオブジェクトの16ビット文字配列をループ中で列挙するためのポインタを安全に取得することはできません。

- ネイティブコードがJNI呼び出し間でVMオブジェクトへのハンドルを保持したい場合は、明示的な参照管理が必要です。

NDKは、Androidプラットフォームでサポートされている、ごく限られたネイティブAPIおよびライブラリの集合についてのみ、システムヘッダを提供します。典型的なAndroidシステムイメージには、数多くのネイティブ共有ライブラリが含まれていますが、それらはプラットフォームのアップデートおよびリリースによって、根本的に変更されうる実装の詳細である、とされています。

もしAndroidシステムライブラリが明示的にNDKヘッダでサポートされていない場合、アプリケーションはそれらが利用可能であることを前提とすべきではなく、さもなければ今後のさまざまなデバイスにおけるシステムアップデートによって動作しなくなるリスクを負うことになります。

選ばれたシステムライブラリが、安定版のNDK APIの集合に含まれることになります。


III. 実践的なNDK開発:
--------------------

以下は、あなたがAndroid NDKでネイティブコードを開発する方法を示した、非常にラフな概要です:


1/ ネイティブソースを $PROJECT/jni/... に置きます。

2/ NDKビルドシステム向けに あなたのソース群を記述した $PROJECT/jni/Android.mk を書きます。

3/ 任意で: NDKビルドシステム向けに、あなたのプロジェクトの詳細を記述した $PROJECT/jni/Application.mk を書きます。はじめはこれを書く必要はありませんが、1つ以上のCPUをターゲットにしたり、コンパイラやリンカーのフラグをオーバーライドできるようになります(詳細は docs/APPLICATION-MK.TXT を見て下さい)。

4/ あなたのプロジェクト ディレクトリ、あるいはそのサブディレクトリのいずれかで、"$NDK/ndk/build" を実行して、ネイティブコードをビルドします。

最後のステップが成功すると、あなたのアプリケーションが必要とする共有ライブラリが、ストリップされて(訳注: 共有ライブラリの不要な部分が削除されるものと思われる)、アプリケーションのルート プロジェクト ディレクトリにコピーされます。そうしたら、あなたは、通常の方法で、最終的な.apkを生成する必要があります。

さて、少し詳しく見ていきましょう:


III.1/ NDKをconfigureする:
- - - - - - - - - - - - -

以前のバージョンでは、 'build/host-setup.sh' スクリプトを実行して、NDKをconfigureする必要がありました。このステップはリリース4 (すなわちNDK r4) で削除されました。


III.2/ CおよびC++のソースを配置する:
- - - - - - - - - - - - - - - - - -

あなたのネイティブソースを以下のディレクトリ以下に配置します:

$PROJECT/jni/

この $PROJECT は、あなたのアプリケーション プロジェクトのパスです。

あなたは、'jni' の内容をどのように編成するかを、自由に決定できます。そのディレクトリ名や構成は、最終的に生成されるアプリケーション パッケージには影響しないため、アプリケーションのパッケージ名のように com.. のような疑似ユニーク名を使用する必要はありません。

CおよびC++のソースがサポートされている、という部分に注意して下さい。NDKでサポートされているデフォルトのC++ファイル拡張子は '.cpp' ですが、他の拡張子も同様に処理できます(詳細は docs/ANDROID-MK.TXT を見て下さい)。

あなたのソースは、Android.mk ファイルを調整することで、別の場所に保存しておくことも可能です(以下を参照)。


III.3/ Android.mkビルドスクリプトを書く:
- - - - - - - - - - - - - - - - - - - -

Android.mkファイルは、あなたのソース群をNDKビルドシステム向けに記述する、小さなビルドスクリプトです。その文法の詳細は docs/ANDROID-MK.TXT のファイルで説明されています。

簡単に言うと、NDKはあなたのソースを「モジュール」にグループ化します。ここで、各モジュールは以下のいずれかになります:


- 静的ライブラリ
- 共有ライブラリ

あなたは、さまざまなモジュールを、1つのAndroid.mkで定義することが出来ます。あるいは、さまざまなAndroid.mkファイルを書いて、それぞれで1つのモジュールを定義することも出来ます。

単一のAndroid.mkが、ビルドシステムによって何度も解析されることがあるため、特定の変数が定義されていないと想定しないよう、注意して下さい。デフォルトでは、NDKは以下のビルドスクリプトを探索します:


$PROJECT/jni/Android.mk

もしAndroid.mkファイルをサブディレクトリで定義したい場合は、最上位のAndroid.mkで明示的にそれらを取り込むべきです。これを行うためのヘルパー関数もありますので、これを使うと良いでしょう:


include $(call all-subdir-makefiles)

これは、現在のビルドファイルのパス以下のサブディレクトリにある全てのAndroid.mkファイルを、取り込みます。


III.4/ Application.mkビルドファイルを書く(任意):
- - - - - - - - - - - - - - - - - - - - - - - - -

Android.mkファイルは、ビルドシステム向けにあなたのモジュールを記述するものでしたが、Application.mkファイルは、あなたのアプリケーション自身について記述するものです。このファイルで出来ることについては、 docs/APPLICATION-MK.TXT のドキュメントを見て下さい。これには、以下のものが含まれます:


- あなたのアプリケーションが要求するモジュールの正確なリスト

- 生成されるマシンコードのCPUアーキテクチャ

- モジュールのビルドに適用すべき、リリースあるいはデバッグのビルドの指示や、特定のC/C++コンパイラ フラグその他のオプション情報

このファイルは任意です: デフォルトでは、NDKは単にAndroid.mk(およびそこで取り込まれた全てのmakefile)でリストされた*全ての*モジュールをビルドし、ターゲットをデフォルトのCPI ABI (armeabi) とします。

Application.mkを使用する方法は2つあります:


- $PROJECT/jni/Application.mk として配置すると、 'ndk-build' スクリプト(詳細は後述)が自動的に拾い上げてくれます。

- $NDK/apps//Application.mk 以下に置きます。ここで、$NDK にはあなたのNDKのインストール パスを指定します。そうしたら、"make APP=" を NDKディレクトリから起動します。

これは Android NDK r4以前で、このファイルを使用する方法でした。これは現在でも互換性の理由からサポートされていますが、わたしたちは最初の方法を使用することを強く推奨します。こちらの方がずっと簡単で、NDKのインストール ツリーのディレクトリを変更する必要が無いためです。

繰り返しますが、この内容の完全な説明については、 docs/APPLICATION-MK.TXT を見て下さい。


III.5/ NDKビルドシステムを呼び出す:
- - - - - - - - - - - - - - - - -

NDKでマシンコードをビルドする、好ましい方法は、Android NDK r4で導入された 'ndk-build' スクリプトを使用することです。第二の、後方互換の方法として、'$NDK/apps' サブディレクトリの作成による方法を用いることもできます。

いずれの場合も、ビルドが成功した場合は、最終的にあなたのアプリケーションで必要となる、ストリップされたバイナリ モジュール(つまり共通ライブラリ)が、アプリケーションのプロジェクト パスにコピーされます(ストリップされないバージョンもデバッグの目的で保存されることに注意して下さい。ストリップされていないバージョンをデバイスにコピーする必要はありません)。


1: 'ndk-build' コマンドを使用する:
----------------------------------

NDKインストールパスの一番上にある 'ndk-build' スクリプトは、あなたのアプリケーション プロジェクトのディレクトリ(つまり、あなたのAndroidManifest.xmlが存在する場所)、あるいは任意のサブディレクトリで、そのまま呼び出せます。
例えば:


cd $PROJECT
$NDK/ndk-build

これで、NDKビルドスクリプトが立ち上がり、あなたの開発システムおよびアプリケーション プロジェクトファイルを自動的に推測して、何をビルドするかを決定します。

例えば:


ndk-build
ndk-build clean -> 生成されたバイナリをクリーンします
ndk-build -B V=1 -> 完全なビルドを強制し、コマンドを表示します

デフォルトでは、任意のファイル $PROJECT/jni/Application.mk と、必須のファイル $PROJECT/jni/Android.mk を期待します。

成功時には、生成されたバイナリモジュール(つまり共有ライブラリ)をあなたのプロジェクト ツリーのしかるべき場所にコピーします。そうしたら、Androidアプリケーションパッケージ全体を、通常の 'and' コマンドや、ADT Eclipseプラグインを使用して、再ビルドできます。

このスクリプトが何を行い、どんなオプションを受け取ることが出来るかについての、完全な説明は、docs/NDK-BUILD.TXT を見て下さい。


2. $NDK/apps//Application.mkを使用する:
---------------------------------------------

このビルド方法は、Android NDK r4以前にサポートされていた唯一のものであり、互換性の理由でのみサポートされています。わたしたちは今後のNDKのリリースではレガシーサポートを削除するかもしれないので、あなたが可及的速やかに 'ndk-build' コマンドを使用する方法に移行することを強く推奨します。

このビルドは以下の手順を要求します:


1. $NDK/apps// と命名されたサブディレクトリを、(あなたのプロジェクトパスではなく)NDKインストール ディレクトリ以下に作成する。

は、NDKビルドシステム向けに、あなたのアプリケーションを記述する、任意の名前です(空白文字は許容されません)。

2. $NDK/apps//Application.mk を作成します。これは、あなたのアプリケーション プロジェクトのディレクトリを示す APP_PROJECT_PATH の定義を要求します。

3. コマンドライン上でNDKインストール パスに移動して、トップレベルのGNUMakefileを以下のようにして呼び出します:


cd $NDK
make APP=

この結末は、中間生成ファイルが $NDK/out/apps// 以下に配置されるという点を除いて、最初の方法と同じことになります。


IV. あなたのアプリケーション パッケージを再ビルドする:
-----------------------------------------------------

NDKでバイナリを生成した後、あなたは、Androidアプリケーション パッケージのファイル (.apk) を、通常の方法すなわち 'ant' コマンドあるいはADT Eclipseプラグインで、再ビルドする必要があります。

詳細はAndroid SDKのドキュメントを見て下さい。新しい .apk にはあなたの共有ライブラリが含まれ、ターゲットデバイス上にパッケージをインストールした時は、それらはシステムによって自動的に解凍されます。


V. デバッグのサポート:
---------------------

NDKは、'ndk-gdb' と呼ばれる、あなたのアプリケーションをネイティブ デバッグするセッションを簡単に立ち上げるヘルパースクリプトを提供しています。

ネイティブデバッグは、Android 2.2以降を実行している製品デバイスで*のみ*実行でき、これにはあなたのアプリケーションがデバッグ可能である限りは、ルートあるいは特権アクセスを要求しません。

詳細は、 docs/NDK-GDB.TXT を読んで下さい。簡単に言うと、ネイティブ デバッグは、以下の簡単な手順に従って行えます:


1. あなたのアプリケーションがデバッグ可能であることを確認する(例えば、AndroidManifest.xmlandroid:debuggable を "true" に設定する)

2. アプリケーションを 'ndk-build' でビルドして、それをデバイス/エミュレータにインストールする

3. アプリケーションを起動する

4. 'ndk-gdb' をあなたのアプリケーション プロジェクトのディレクトリから実行する

これで、gdbのプロンプトが表示されます。ここで使えるコマンドについては、GDBユーザーマニュアルを見て下さい。