2014年3月14日金曜日

JNI (Java Native Interface) (2) 最も簡単なコードを書いてみる

ここでは、最も簡単なコードを書いてみましょう。
世界が広がる「Hello World!」です。

ただ実行するだけでなく、出来るだけ簡単に build 出来る環境も作成しましょう。

流れ


Java と c や c++ の両方の経験があれば、難しい処理は出てきません。

Java だけしか経験が無い場合、少し戸惑うかもしれませんが、知らなかったり、わからない部分は c や c++ のコードを実行するのに必要な作業と思えばよいと思います。

まず、Javaのコードを書きます。
その際、メソッドに nativeキーワード を指定して、nativeメソッド宣言します。
Javaクラスにはnativeメソッドの本体は定義しないので注意してください。

javah 使って、nativeメソッド毎の関数プロトタイプが記述された ヘッダファイル を生成します。

その関数プロトタイプの記述に従って、c や cpp で本体の関数を記述します。

関数プロトタイプには、入力(仮引数) と 出力(戻り値) を定義するので、native関数はそれに従って記述する必要があります。

本体のnative関数は、プラットフォーム固有のコンパイラとリンカを使用してコンパイラします。ここは、プラットフォーム毎に手順が違うと思います。

JVMは、それぞれの関数プロトタイプに定義された呼び出しプロトコルを使って、nativeメソッドを実行します。

これが、一連の流れです。
ここでは、native メソッドのプログラムは C や C++ で考えることにします。

ネイティブコードへのインターフェース記述

Javaコード と native のコードを統合するときの課題は、Javaのnativeメソッドの仮引数を、既存のAPIの仮引数にマッピングすることです。

一般の解決法は以下の 2 つです。

  1. 従来のデータを Java で設計し、そのデータを扱えるうまいメソッドを定義する
  2. 既存のインターフェースを、自分の Java のnativeメソッド宣言に直接マップ
どちらも難しそうです。
後者は、Cの構造体をJavaのオブジェクトで置き換えるために適用できる方法ですが、後で詳しく見ていくことにします。


順番に見ていきましょう。


Javaコードの生成

Javaクラスに、nativeメソッドを定義します。
例えば以下の様な感じです。





javac でクラスファイルを生成

IDEを使用していれば、ここは意識しないところですね。

簡単に以下の様にコマンドすれば出来ます。

% javac NativeSampleMethod.java

javahでインクルードするヘッダファイルを生成

nativeメソッドの宣言が入ったJavaクラスファイルを作成したら、対応する ANSI C関数プロトタイプを生成します。

native宣言の入ったすべてのクラスファイルで、javah を走らせます。

Javaクラスファイルの場所は、環境変数 CLASSPATH、javahのオプション -classpath で指定します。

% javah -jni NativeSampleMethod

生成されたヘッダファイルは、nativeメソッドを実装したソースファイルにインクルードが出来る。

ヘッダファイルの名前は、入力したクラスの完全修飾された名前で決まります。つまり、パッケージ名までを含んだ一意な名前になります。


例えば以下のようになります。


これはjp.sprix.jniパッケージの配下にjavaファイルを置いている場合です。

次に、nativeコードを記述します。


以下の3つの制限に従っていることに注意しましょう。

  1. nativeメソッドの実装関数は、最初の引数が JNIEnvへのポインタ、2番目がオブジェクト参照
  2. 入力引数の型はJNIが定義する
  3. 戻り値の型はJNIが定義する
  4. 関数プロトタイプが含む ヘッダファイルをインクルードしなければならない

ライブラリのビルド

プラットフォーム固有になる部分です。

Linuxであれば .so 、Windowsであれば .dll をビルドします。


  1. nativeコードをコンパイルし、jni.hをインクルードするためにふさわしい場所はすべて指定
  2. nativeライブラリをビルド


今回は、ant contrib を使用して、ビルドしましょう。
詳細な方法は、以前のブログに書いています。

http://masabloggers.blogspot.jp/2013/05/jni-javacpp.html



今回必要になるbuild.xmlは以下です。


この構成に必要となる構造は以下です。

src -  javaパッケージ、javaソースコード、ANSI 関数プロトタイプヘッダファイル格納
dist - 動的ライブラリの格納ディレクトリ
jnih - ANSI 関数プロトタイプヘッダファイル、native実装ファイル
build.xml

build.xmlファイルを読めば、大体のディレクトリ構造はわかると思います。
また、gccコンパイルに必要なインクルードディレクトリやリンカーの設定もしてあります。

これでbuildしたxmlファイルは、実行時に以下のjava オプションでnativeライブラリの場所を指定すます。

LD_LIBRARY_PATH

以上が、Hello World と ビルド方法についてです。


次は、起きやすい問題やその対処について書こうと思います。

0 件のコメント:

コメントを投稿