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()) {



  1. 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?

  2. Hi, I tried this but when try to call this from one of JNI call this say {error: “‘callback’ was not declared in this scope”}.

    Any suggestion here!

Leave a comment

Your email address will not be published.