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