作成 2002/12/22
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++のようです。 ここでは、以下の環境で試しました。
作成は以下の手順で行います。
こちら のページを見ると大変わかりやくていいでしょう。
参考にして作ってみました。 あんまり同じでもアレなんで、Javaクラスをパッケージ付きにして、 Nativeメソッドをstaticにしてみました。
メソッドをstaticで宣言すると、C側のメソッド引数が、 引数がjobjectからjclassに変わります。 パッケージを付けると、 javahしたメソッドにパッケージ文字列が着きます。 Java実行時は、環境変数Pathの場所にdllを置いてもいいですが、 VMのjava.library.pathオプションで明示的に指定することもできます。
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);
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側)で文字化け解決を行えました。
何が難しいって、慣れないdllの作成ですね。 最初Cygwin/gccでやってたんですが、 dllを作るので挫折しまし、 結局、参考ページを見てBorlandのコンパイラを使いました。
インターフェイスの変更に依存せず、C側であんまりめんどくさくない パラメータ渡しはどうすべきか?