1. 概述

在这个简短的教程中,我们将探讨JNI(Java Native Interface)中的RegisterNatives()方法,它用于在Java和C++函数之间建立映射关系。

首先,我们将解释RegisterNatives()的工作原理,然后展示它如何在java.lang.ObjectregisterNatives()方法中使用。最后,我们将演示如何在自己的Java和C++代码中使用这些功能。

2. JNI RegisterNatives 方法

JVM有两种方式来查找并链接与Java代码的原生方法。 第一种是以特定方式调用原生函数,以便JVM能够找到它。另一种是使用JNI的RegisterNatives()方法

顾名思义,RegisterNatives()方法通过传入的类将原生方法注册到JVM。通过这种方法,我们可以随意命名我们的C++函数

实际上,java.lang.ObjectregisterNatives()方法采用了第二种方式。让我们来看看OpenJDK 8中java.lang.ObjectregisterNatives()方法在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]));
}

首先,method[]数组被初始化,用于存储Java和C++函数名称之间的映射。然后,我们看到一个以非常特定方式命名的方法,Java_java_lang_Object_registerNatives

通过这种方式,JVM能够将它链接到java.lang.ObjectregisterNatives()原生方法。在方法内部,method[]数组被用于RegisterNatives()方法的调用。

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

3. 使用RegisterNatives方法

我们从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();
    }
}

我们定义了两个原生方法,register()sayHello()。前者将使用RegisterNatives()方法来注册一个自定义的C++函数,当调用原生的sayHello()方法时将使用这个函数。

现在来看Java的register()原生方法的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]));
}

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

接着,我们看到一个名为Java_com_baeldung_jni_RegisterNativesHelloWorldJNI_register的函数,必须这样命名,以便JVM能找到并将其与Java代码链接起来。

这个函数有两个作用:首先,它找到所需的Java类,然后调用RegisterNatives()方法并将类和映射数组传递给它。

现在,我们可以随时调用第二个原生方法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());
}

我们使用了一个更短、更有意义的名字,而不是完整的完全限定名称。

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

Hello from registered native C++ !!

4. 总结

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

如往常一样,文章的完整源代码可在GitHub上获取。