JNI
JNI
Javaにおいて、C/C++のモジュールを呼び出すことができる、または、逆にC/C++からJavaの機能を呼び出すことができるインターフェースを「JNI」(Java Native Interface)と言います。JNIを使用することにより、JavaではタッチできなかったOSネイティブな処理や速度の必要な処理をC/C++言語で行ったりできるようになります。
以下に、その手順を説明します。ここでは、JavaからC言語の関数を呼び出す方法を記述していきます。
Javaソースを記述
呼び出す側のJavaのソース「JNITest.java」を以下のようにします。
[ JNITest.java ]
public class JNITest { static { System.loadLibrary("JNITest"); } public native String getText(); public native int add(int a,int b); public static void main(String[] args) { JNITest jni = new JNITest(); String str; //C言語に関連付けた関数「getText()」を呼び出し str = jni.getText(); System.out.println(str); //C言語に関連付けた関数「add()」を呼び出し int a,b; a=51; b=23; System.out.println("加算 : "+ a + "+" + b + "=" + jni.add(a,b)); } }
この場合は、(Windowsの場合)「JNITest.dll」をライブラリとして読み込みます。これの作成については後述します。Linuxの場合は、拡張子が「so」になるでしょうか。そして、「native」指定で関数「String getText()」と「int add(int a,int b)」を指定しています。この「native」が記述された関数が、JavaからCを呼び出す関数の宣言となります。ここではC言語側から、「getText()」で"HelloWorld!!"の文字列を返し、「add(int a,int b)」でa+bの計算結果を返すものとします。
「JNITest jni = new JNITest();」でネイティブの関数呼び出しのオブジェクトを生成して、各関数を呼び出しています。
C言語用ヘッダの出力
そして、C言語部分のモジュールを作成するための準備をします。まずは、以下のように普通にJavaソースをコンパイルします。
javac JNITest.java
次に、C言語で使用するヘッダファイルを生成します。
javah -jni -o JNITest.h JNITest
「-o JNITest.h」は出力するC言語のヘッダ名を指定していますが、省略もできます。省略した場合は、その後の「クラス名.h」が出力されることになります。最後の「JNITest」が、「JNITest.java」をコンパイルした後に生成されるクラス名を指定しています。
これで、以下のような「JNITest.h」が自動生成されます。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class JNITest */ #ifndef _Included_JNITest #define _Included_JNITest #ifdef __cplusplus extern "C" { #endif /* * Class: JNITest * Method: getText * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_JNITest_getText (JNIEnv *, jobject); /* * Class: JNITest * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_JNITest_add (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif
C言語側の準備
次は、C言語側の準備をします。C言語で提供するモジュールは、「共有ライブラリ(DLL)」である必要があります。VC++/BCCなどで、DLLを作成する設定を行ってください。そして、インクルードファイル検索パスに「Javaのインストールディレクトリ\include」と(Windowsの場合は)「Javaのインストールディレクトリ\win32」を加えます。ライブラリファイルの検索パスに「Javaのインストールディレクトリ\lib」を加え、「jvm.lib」をリンク時の構成に加えてください。
そして、先ほど自動生成した「JNITest.h」を、コンパイラが検索できるインクルードのパス位置にコピーしてきてください。
C言語側のソースの記述
C言語側のソースは、以下の感じになります。
[JNITest.cpp]
#include <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "JNITest.h" //javahで生成したヘッダを指定 BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } JNIEXPORT jstring JNICALL Java_JNITest_getText(JNIEnv *env, jobject o) { jstring str = env->NewStringUTF("HelloWorld!!"); return str; } JNIEXPORT jint JNICALL Java_JNITest_add(JNIEnv *env, jobject o, jint a, jint b) { jint sum; sum = a + b; return sum; }
このときの関数名「Java_JNITest_getText」「Java_JNITest_add」や引数などの記述は、ヘッダ「JNITest.h」よりそのままコピーしてきています。「Java_JNITest_getText」は、"HelloWorld!!"の文字列を返し、「Java_JNITest_add」は、「引数の(a+b)」を結果として返しています。
ちなみに、Windows以外のOSでは、「DllMain」の関数記述はいらないです。これをコンパイルすると、無事「JNITest.dll」が生成されます。
JNIを使ったJavaプログラムの実行
生成した「JNITest.dll」を、コマンドラインでJavaを実行するときに参照できる位置(PATHの通った位置)、もしくはカレントディレクトリにコピーします。そして、一番始めに生成したJavaの「JNITest」を実行します。
javac JNITest
如何でしょうか?
HelloWorld!! 加算 : 51+23=74
と表示されましたか?簡単な処理をJavaからC言語呼び出しで行ったのですが、もっと時間のかかるような処理をC言語側で実行することで、 Javaのネックとなる「速度」の問題を緩和することができるようになるかと思います。総合開発環境の「Eclipse」もJNI経由で「SWT」を呼び出しているようですね。なので、動作速度がSwingに比べて快適です。
日本語文字列を返す
以下はおまけですが、日本語文字列(SHIFT-JIS)を返す場合は、その変換処理をC言語側で行ってあげる必要があります。JavaはUTF-8で文字列を扱いますので、「WindowsのC言語→Java」へは、「SHIFT-JIS→UTF-8」の変換処理が必要です。変換方法は、以下が一番楽です。
例えば、先ほどの「getText」で「はろーわーるど」と返す場合は、以下でOKです。
JNIEXPORT jstring JNICALL Java_JNITest_getText(JNIEnv *env, jobject o) { const char *pBuff = "はろーわーるど"; int len = strlen(pBuff); //Unicode文字列の長さを取得 int uLen = MultiByteToWideChar( CP_ACP, 0, pBuff, len , NULL , 0); WCHAR *retBuff = new WCHAR[uLen]; //Unicode文字列に変換する MultiByteToWideChar( CP_ACP, 0, pBuff, len , retBuff , uLen); //Javaの文字列生成 jstring ret = env->NewString((jchar *)retBuff , uLen); delete retBuff; return ret; }
ただ、OS依存の「MultiByteToWideChar」はWindows以外では使えませんので、各OS対応の変換処理を行うか、Javaの「java.lang.String」をマッピングして変換してやるといいかもしれません。#昔、仕事で「java.lang.String」を呼び出して対応した覚えがあるのですが、ソースが見つからない(^_^;;
Future's Laboratory 技術格納庫 2004-2013 Yutaka Yoshisaka.