registerNatives()方法有什么作用?


Answers:


114

其他答案在技术上是正确的,但对于没有JNI经验的人来说不是很有用。:-)

通常,为了让JVM找到您的本机函数,必须以某种方式命名它们。例如,对于java.lang.Object.registerNatives,相应的C函数被命名为Java_java_lang_Object_registerNatives。通过使用registerNatives(或更确切地说,是JNI函数RegisterNatives),您可以随便命名C函数。

以下是相关的C代码(来自OpenJDK 6):

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]));
}

(注意,Object.getClass不在列表中;它仍将以的“标准”名称来调用Java_java_lang_Object_getClass。)对于列出的函数,相关的C函数如该表中所列,这比编写一堆转发函数要方便。

如果将Java嵌入到C程序中并希望链接到应用程序本身内的功能(而不是共享库内),或者不以其他方式“导出”正在使用的功能,则注册本机功能也很有用。通常不会通过标准方法查找机制找到。注册本机函数还可以用于将本机方法“重新绑定”到另一个C函数(例如,如果您的程序支持动态加载和卸载模块,则很有用)。

我鼓励每个人都阅读JNI书,其中讨论了更多内容。:-)


1
您编写了完全修饰的JNI函数名,这使其变得混乱:Java_java_lang_Object_registerNativesVM自动调用了该函数名,或者c / c ++代码必须调用该函数,并且不需要所有Java gobblygook
Pavel P

@Pavel默认情况下,以C名称(您的C端代码必须导出)搜索methodName类中的本机方法。您可以调用的方法来覆盖此链接。换句话说,如果您不首先使用,则必须使用“ java gobblygook所有”。fully.qualified.ClassNameJava_fully_qualified_ClassName_methodNameJNIEnvRegisterNativesRegisterNatives
克里斯·杰斯特·杨

嗯,好的,所以仅导出1个函数,Java_java_lang_Object_registerNatives我实际上可以通过检查的值链接我所有的JNI东西cls。我错过了registerNatives实际上是Java本身的一部分。所以...当要使用某个类时,JVM用来链接本机的动作顺序是什么?看来,如果尚未注册本机(从c / c ++),则JVM会检查所有这些导出的名称,如果不存在,则尝试使用Object.registerNatives(或者相反)。顺便说一句,jni图书链接已失效。
Pavel P

如果尚未调用RegisterNatives,并且导出的名称不可用,则在尝试调用该方法时会出现运行时错误。Object.registerNatives是OpenJDK实现的内部方法,java.lang.Object从其类初始化程序调用,其唯一目的是注册java.lang.Object仅与本机方法有关的方法。如果您正在编写自己的本机库,则要由您自己注册库的本机方法(您希望这样做)。
克里斯·杰斯特·杨

11

可能有点令人困惑的是,java.lang.Object.registerNatives上一个答案中显示的代码仅是如何注册本机函数的示例。这是(在OpenJDK的实现中)为Object类注册本机函数的代码。要为您自己的类注册本机函数,必须RegisterNatives从您自己的库中的本机代码调用JNI函数。这听起来可能有点循环,但是有几种方法可以打破循环。

  1. 遵循该类Object的实现示例:

    一种。在您的Java类中,声明一个命名为registerNatives(或其他任何名称的无关紧要的)本地方法(最好是静态方法)。

    b。在您的本机代码中,定义一个名为的函数Java_<your fully qualified class name>_registerNatives,其中包含对JNI函数的调用RegisterNatives

    C。确保在您的Java代码中,registerNatives在调用其他本机方法之前先调用Java方法。

要么

  1. 采用 JNI_OnLoad

    一种。在您的本机库中定义一个函数jint JNI_OnLoad(JavaVM *vm, void *reserved)。在此函数的主体中,调用JNI函数RegisterNatives

    b。当Java虚拟机JNI_OnLoad由加载时,Java VM将自动查找并调用System.loadLibrary,您应该已经在调用它了,可能是在类的静态初始化器中。(您可以env通过GetEnvvm指针指向的表中调用该函数来获得所需的指针。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.