1. 概述
在这个简短的教程中,我们将探讨JNI(Java Native Interface)中的RegisterNatives()
方法,它用于在Java和C++函数之间建立映射关系。
首先,我们将解释RegisterNatives()
的工作原理,然后展示它如何在java.lang.Object
的registerNatives()
方法中使用。最后,我们将演示如何在自己的Java和C++代码中使用这些功能。
2. JNI RegisterNatives
方法
JVM有两种方式来查找并链接与Java代码的原生方法。 第一种是以特定方式调用原生函数,以便JVM能够找到它。另一种是使用JNI的RegisterNatives()
方法。
顾名思义,RegisterNatives()
方法通过传入的类将原生方法注册到JVM。通过这种方法,我们可以随意命名我们的C++函数。
实际上,java.lang.Object
的registerNatives()
方法采用了第二种方式。让我们来看看OpenJDK 8中java.lang.Object
的registerNatives()
方法在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.Object
的registerNatives()
原生方法。在方法内部,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上获取。