How to use JNI’s RegisterNatives() method? – 如何使用JNI’的RegisterNatives()方法?

最后修改: 2022年 2月 19日

1. Overview

1.概述

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.

在这个简短的教程中,我们将看看JNI RegisterNatives()方法,它被用来在Java和C++函数之间创建映射。

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.

首先,我们将解释JNI的RegisterNatives()如何工作java.lang.ObjectsregisterNatives()方法中的应用。最后,我们将展示如何在我们自己的 Java 和 C++ 代码中使用该功能。

2. JNI RegisterNatives Method

2.JNI RegisterNatives方法

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.

JVM有两种方法可以找到并将native方法与Java代码连接起来。第一种方法是以特定的方式调用本地函数,以便JVM可以找到它。另一种方法是使用JNI的RegisterNatives()方法

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.

顾名思义,RegisterNatives() 用作为参数传递的类来注册本地方法。通过使用这种方法,我们可以随心所欲地命名我们的C++函数

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:

事实上,java.lang.Object’s registerNatives()方法采用的是第二种方法。让我们看看java.lang.Object’s registerNatives()方法在OpenJDK 8中的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.

首先, method[]数组被初始化以存储Java和C++函数名称之间的映射。然后,我们看到一个以非常特殊方式命名的方法,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.

通过这样做,JVM能够将其与本地的java.lang.Objects RegisterNatives()方法联系起来。在它里面,method[]数组被用于RegisterNatives()方法的调用中。

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

现在,让我们看看如何在我们自己的代码中使用它。

3. Using the RegisterNatives Method

3.使用RegisterNatives方法

Let’s start with the Java class:

让我们从Java类开始。

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.

我们定义了两个本地方法,register()sayHello()。前者将使用RegisterNatives()方法来注册一个自定义的C++函数,以便在本地sayHello()方法被调用时使用。

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

让我们看看Java的register()native方法的C++实现。

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.

java.lang.Object例子类似,我们首先创建一个数组来保存Java和C++方法之间的映射。

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. 

然后,我们看到一个以完全合格的Java_com_baeldung_jni_RegisterNativesHelloWorldJNI_register名称调用的函数。不幸的是,它必须以这种方式被调用,以便JVM能够找到它并将其与Java代码联系起来。

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.

该函数做了两件事。首先,它找到所需的Java类。然后,它调用RegisterNatives()方法并将类和映射数组传给它。

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

现在,我们可以随意调用第二个本地方法,sayHello()

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:

最后,让我们运行main()类中的RegisterNativesHelloWorldJNI方法。

Hello from registered native C++ !!

4. Conclusion

4.总结

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.

在这篇文章中,我们讨论了JNI的RegisterNatives()方法。首先,我们解释了java.lang.Object.registerNatives()方法在幕后的作用。然后,我们讨论了为什么使用JNI的RegisterNatives()方法可能是有用的。最后,我们展示了如何在我们自己的 Java 和 C++ 代码中使用它。

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

一如既往,该文章的完整源代码可在GitHub上获得