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