メインコンテンツへスキップ

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

NOTE
この記事は最終更新日から1年以上が経過しています。内容が古くなっている可能性があります。

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先生に頼りすぎるのも考え物ですね。

関連記事

Javaのprotectedの意味

Javaのアクセス修飾子protectedで少しハマったので、調べた内容をメモしておきます。

JNIでenumを扱う

Java 5.0から導入されたJavaのenumですが、JNIを経由したnativeメソッド内ではどのように扱えばよいのでしょうか?これについては、JNI仕様にも明記されていないようです。必要があったので、ちょっと調べてみました。

スレッド間でカウンタを共有する(再び)

これまでも話してきたとおり、スレッド間でオブジェクトを共有することはハイコストなので、可能な限りオブジェクトを共有しない設計にすることが性能においても品質においても重要です。しかし、どうしてもオブジェクトを共有したい場合は、昨今の処理系にはそれを保証する機能が用意されているのでそれを使って安全を担保します。