Java Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

> CHECK OUT THE COURSE

1. Overview

In this short tutorial, we'll take a look at the JNI RegisterNatives() method, which is used to create mappings between Java and C++ functions.

First, we'll explain how JNI RegisterNatives() works. Then, we'll show how it's used in the java.lang.Object'registerNatives() method. Finally, we'll show how to use that functionality in our own Java and C++ code.

2. JNI RegisterNatives Method

The JVM has two ways to find and link native methods with Java code. The first one is to call a native function in a specific way so that the JVM can find it. Another way is to use the JNI RegisterNatives() method.

As the name suggests, RegisterNatives() registers native methods with the class passed as an argument. By using this approach, we can name our C++ functions whatever we want.

In fact, java.lang.Object'registerNatives() method uses the second approach. Let's see a java.lang.Object'registerNatives() method implementation from OpenJDK 8 in C:

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

Firstly, the method[] array is initialized to store mappings between Java and C++ function names. Then, we see a method named in a very specific way, Java_java_lang_Object_registerNatives.

By doing so, the JVM is able to link it to a native java.lang.Object'registerNatives() method. Inside it, the method[] array is used in the  RegisterNatives() method call.

Now, let's see how can we use it in our own code.

3. Using the RegisterNatives Method

Let's start with the Java class:

public class RegisterNativesHelloWorldJNI {

    public native void register();
    public native String sayHello();

    public static void main(String[] args) {
        RegisterNativesHelloWorldJNI helloWorldJNI = new RegisterNativesHelloWorldJNI();
        helloWorldJNI.register();
        helloWorldJNI.sayHello();
    }
}

We define two native methods, register() and sayHello(). The former will use the RegisterNatives() method to register a custom C++ function to use when the native sayHello() method is called.

Let's see the C++ implementation of Java's register() native method:

static JNINativeMethod methods[] = {
  {"sayHello", "()Ljava/lang/String;", (void*) &hello },
};


JNIEXPORT void JNICALL Java_com_baeldung_jni_RegisterNativesHelloWorldJNI_register (JNIEnv* env, jobject thsObject) {
    jclass clazz = env->FindClass("com/baeldung/jni/RegisterNativesHelloWorldJNI");

    (env)->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(methods[0]));
}

Similarly to the java.lang.Object example, we first create an array to hold mappings between Java and C++ methods.

Then, we see a function called with a fully qualified Java_com_baeldung_jni_RegisterNativesHelloWorldJNI_register name. Unfortunately, it must be called this way in order for the JVM to find and link it with Java code. 

The function does two things. First, it finds the desired Java class. Then, it calls the RegisterNatives() method and passes it the class and the mappings array.

Now, we can call the second native method, sayHello(), whatever we want:

JNIEXPORT jstring JNICALL hello (JNIEnv* env, jobject thisObject) {
    std::string hello = "Hello from registered native C++ !!";
    std::cout << hello << std::endl;
    return env->NewStringUTF(hello.c_str());
}

Instead of the fully qualified name, we used a shorter, meaningful name.

Finally, let's run the main() method from the RegisterNativesHelloWorldJNI class:

Hello from registered native C++ !!

4. Conclusion

In this article, we discussed the JNI RegisterNatives() method. Firstly, we explained what the java.lang.Object.registerNatives() method does under the hood. Then, we discussed why using the JNI RegisterNatives() method might be useful. Finally, we showed how to use it in our own Java and C++ code.

As always, the complete source code of the article is available over on GitHub.

Java bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!