JNIにさわる

作成 2002/12/22

JNIって?

JNIはJava Native Interfaceの略で、 Javaと、Windows、LinuxなどのNativeコードを結ぶAPIです。 JREの中でもJNIが利用されています。 JavaがWrite Once Run Anywhereなのは、 各プラットフォームごとのJNI部分をJREの中でやってくれているからです。

Nativeのコード(Windows dllなど)を利用したい場合、JNIを利用します。 JNIのNative側のコードは特に限定されていませんが、 ほとんどの場合C/C++のようです。 ここでは、以下の環境で試しました。

作成は以下の手順で行います。

  1. Javaクラスの作成、コンパイル
  2. javahでC++ヘッダファイルの作成
  3. Nativeコードの記述とメイク

Hello JNI

こちら のページを見ると大変わかりやくていいでしょう。

参考にして作ってみました。 あんまり同じでもアレなんで、Javaクラスをパッケージ付きにして、 Nativeメソッドをstaticにしてみました。

メソッドをstaticで宣言すると、C側のメソッド引数が、 引数がjobjectからjclassに変わります。 パッケージを付けると、 javahしたメソッドにパッケージ文字列が着きます。 Java実行時は、環境変数Pathの場所にdllを置いてもいいですが、 VMのjava.library.pathオプションで明示的に指定することもできます。

Echo JNI

Nativeメソッドに引数をつけてみます。

EchoJNI.java

public native static String sayEcho(String message);

プリミティブ引数は問題ないですが、 jstringをchar*に変換した場合は、(メモリが確保されているので)開放の必要があります。 ガベージコレクションって便利でしたね。。。

EchoJNI.cpp

const char *s = env->GetStringUTFChars(message, NULL);
printf("message=%s \n", s);
env->ReleaseStringUTFChars(message, s);

javaへのアクセス

NativeからJavaのフィールド、メソッドの呼び出し、ゲット、セットが可能です。 staticとインスタンスかで、利用するメソッド名が微妙に違います。

CallJNI.cpp

jclass cl = env->GetObjectClass(instance);

jmethodID method = env->GetMethodID(cl, "getMessage", "()Ljava/lang/String;");
jstring text1 = (jstring)env->CallObjectMethod(instance, method);
printMessage(env, text1);

jfieldID field = env->GetFieldID(cl, "message", "Ljava/lang/String;");
jstring text2 = (jstring)env->GetObjectField(instance, field);
printMessage(env, text2);

日本語文字化け

EchoJNIで引数に日本語を指定した場合、 cpp内のprintfで文字化けが生じます。 これはJNIを通して渡すjstringがUTF-8になってしまうからです。

解決策としては、いつもの new String(String#getBytes("Shift_JIS"), "ISO-8859-1"); が使えます。

この処理を、Java側で行って、byte配列で渡して、 ネイティブ側でcharを作るか、 未エンコードのjstringをネイティブ側で変換できます。

この本 に書いてあるやり方(Native側)で文字化け解決を行えました。

ParamJNI.cpp

感想

何が難しいって、慣れないdllの作成ですね。 最初Cygwin/gccでやってたんですが、 dllを作るので挫折しまし、 結局、参考ページを見てBorlandのコンパイラを使いました。

インターフェイスの変更に依存せず、C側であんまりめんどくさくない パラメータ渡しはどうすべきか?

参考


TOP