JNIで起動したJavaVMからnativeメソッドをコールバック

JNIを使ってCなどのnativeプロセスからJavaVMを起動し、さらにそのJavaVMからnativeメソッドをコールバックさせる方法です。Webで調べた際に、意外と情報が見つからなかったのでメモ。

結論から言ってしまうと、RegisterNativesというJNI関数を使用します。この関数は、jclassとJNINativeMethod構造体の配列を引数に取り、Java側のnativeメソッド宣言とnative側のメソッド本体を動的に結びつけます。

具体的な使い方は、以下のサンプルコードを参考にしてください。

まずは、nativeのコールバックメソッドを持つJavaクラスを作成。

package jnitest;

public class JniTest {
	
	// nativeから呼ばれるメソッド
	public boolean entryMethod() {
		System.out.println("Java Method.");
		return callbackMethod();
	}

	// JavaVMから呼び出すメソッド
	private native boolean callbackMethod();
	
}

C(native)側には、Java側に定義したコールバックメソッドに合わせたnativeメソッドを作成し、RegisterNativesメソッドでJavaVMに登録します。


// JavaVMから呼ばれるメソッド
jboolean nativeCallback(JNIEnv* env, jobject obj) {
	fprintf(stdout, "Native Callback Method.\n");
	return JNI_TRUE;
}

int main(void) {

	// JavaVM起動、JNIEnv取得処理(省略)

	// nativeメソッドを登録
	jclass jni_test_class = env->FindClass("jnitest/JniTest");
	JNINativeMethod native_method = {"callbackMethod", "()Z", nativeCallback};
	env->RegisterNatives(jni_test_class, &native_method, 1);

登録を行った後、JavaのentryMethod()を呼び出すと、

	// JniTestオブジェクトを生成
	jmethodID init_method = env->GetMethodID(jni_test_class, "<init>", "()V");
	jobject jni_test_obj = env->NewObject(jni_test_class, init_method);

	// entryMethodを呼び出す
	jmethodID entry_method = env->GetMethodID(jni_test_class, "entryMethod", "()Z");
	env->CallBooleanMethod(jni_test_obj, entry_method);

JavaVMからnativeCallback()が呼び出され、無事、以下の出力が得られます。

Java Method.
Native Callback Method.

余談ですが、上にも書いたように、ネット検索ではこの方法が見つけられませんでした。なので最初は、コールバック関数のポインタをjint型にキャストしてJavaVMのメソッドに渡しておき、JavaVMからnativeメソッドへの引数としてこのjintを渡して、nativeメソッドで再度jnitを関数ポインタにキャストし直してコールバック関数を呼び出すという方法も試したりしていました。

いちおう呼び出しは成功していましたが、どうにもトリッキーすぎるので書籍を買ってきて調べたところ、すぐに標準の方法が見つかった次第です。あまりGoogle先生に頼りすぎるのも考え物ですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*