home / blog

JNI asynchronous callbacks

If you’re using JNI, you may wish to callback a java method asynchronously. For example event handling from a win32 message pump.

package com.adamish;

public class Foo {
  public native void register();
  public void callback(int val) {
    // do stuff
  }
}

Step one is to have a “register” method which lets the C++ code obtain a reference to the java code to enable the callback.

For efficiently we cache the references to the VM, the object, and the method. This means less work per callback.

For the object reference you must convert the local reference to a global reference. Local references only exist for the lifetime of the JNI bound call. Afterwards they cannot be used, and worse than that attempts to use them fail silently – the java code is simply not called.

// cached refs for later callbacks
JavaVM * g_vm;
jobject g_obj;
jmethodID g_mid;

JNIEXPORT jboolean JNICALL Java_com_adamish_Foo_register
	(JNIEnv * env, jobject obj, jlong hwnd) {
                bool returnValue = true;
		// convert local to global reference 
                // (local will die after this method call)
		g_obj = env->NewGlobalRef(obj);

		// save refs for callback
		jclass g_clazz = env->GetObjectClass(g_obj);
		if (g_clazz == NULL) {
			std::cout << "Failed to find class" << std::endl;
		}

		g_mid = env->GetMethodID(g_clazz, "callback", "(I)V");
		if (g_mid == NULL) {
			std::cout << "Unable to get method ref" << std::endl;
		}

		return (jboolean)returnValue;
}

Now, the actual callback using our cached references from the register() method. There’s quite a bit going on here. Since the callback is on another thread the VM context must be attached to the current thread.


void callback(int val) {
	JNIEnv * g_env;
	// double check it's all ok
	int getEnvStat = g_vm->GetEnv((void **)&g_env, JNI_VERSION_1_6);
	if (getEnvStat == JNI_EDETACHED) {
		std::cout << "GetEnv: not attached" << std::endl;
		if (g_vm->AttachCurrentThread((void **) &g_env, NULL) != 0) {
			std::cout << "Failed to attach" << std::endl;
		}
	} else if (getEnvStat == JNI_OK) {
		//
	} else if (getEnvStat == JNI_EVERSION) {
		std::cout << "GetEnv: version not supported" << std::endl;
	}

	g_env->CallVoidMethod(g_obj, g_mid, val);

	if (g_env->ExceptionCheck()) {
		g_env->ExceptionDescribe();
	}

	g_vm->DetachCurrentThread();
}
This entry was posted in geek and tagged , . Bookmark the permalink.

One Response to JNI asynchronous callbacks

  1. Soana says:

    The much better question is: Where does the “jlong hwnd” in the JNI declaration of the register method come from?
    In the Java part register is declared with no arguments, so no arguments (apart from the JNIEnv and the this-object of course) should appear in the JNI declaation, shouldn’t it?

Leave a Reply

Your email address will not be published.


8 × = sixteen

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>