C/C++からJavaのライブラリを呼ぶ with JNI その2

さらにコールバック関数を登録するの巻
フルのソースコードはこちら
参考にしたページ : JNIで起動したJavaVMからnativeメソッドをコールバック(http://blog.bitmeister.jp/?p=1104)


今回は、Cの関数をJava側に登録してコールバック関数として使う場合のお話.
Javaの方は以下のようなクラスHogeBackを想定

package my.hoge;

public class HogeBack {
	private int num;

	private native int callbackMethod(int num);

	public void HogeBack()
	{
		num = 0;
	}

	....

	public int invokeMethod(int num){
		System.out.println("Invoking method");
		return callbackMethod(num);
	}

}

invokeMethod()はテストの便宜上あたらしく追加したメソッド. Cの側からコールバックできることを確認する.

このcallbackMethod()として呼び出されるC側の関数として以下のようなものを用意する
引数に1000足して返すだけ.

jint nativeCallback(JNIEnv* env, jobject obj, jint value){
	printf("Native Callback method\n");
	int result = 1000 + (jint)value;
	return (jint)result;
}

このnativeCallbackをコールバック関数として登録・呼び出すためには, 通常のJVMの起動その他に加えて以下の手順を踏む

  1. nativeCallbackの登録
  2. invokeMethod()の呼び出し

1.nativeCallbackの登録
ネイティブメソッドの登録にはRegisterNativesメソッドを使う.
このときそれぞれのネイティブメソッドについて, そのJava側での代入先の変数名とシグネチャと関数ポインタのセットからなるJNINativeMethodという構造体を用意する必要がある.
今回のnativeCallback()はjava側のc"allbackMethod"に格納し, シグネチャは"(I)I"なので以下のようになる.

	JNINativeMethod n_method = {"callbackMethod", "(I)I", (void *)nativeCallback};

なお注意点としてnativeCallbackの前に(void *)というキャストをしないと以下のように怒られたので上記のようにしている

jnitest.cc:121: error: invalid conversion from ‘jint (*)(JNIEnv*, _jobject*, jint)’ to ‘void*’


用意したこのJNINativeMethod構造体に対してRegisterNatives関数を呼び出すことで登録が行われる.
引数は登録先のクラスと, 先の構造体, 登録するメソッド(関数)の個数.

	jnienv->RegisterNatives(cls, &n_method, 1);

invokeMethod()の呼び出し
テスト用のメソッドinvokeMethod()の呼び出し.
通常通り呼び出せばいいので特に問題はない.

int callbacked = (int)jnienv->CallIntMethod(obj, invokeMethodId, 17);

実行結果
以上のコードを実行すると以下のようにC側のコールバックメソッドが呼ばれる時に表示されるメッセージがちゃんと
出力され、計算結果も(たぶん)ただしく出ている

----call
Invoking method
Native Callback method
----called : result = 1017