如何在JNI中识别方法?

时间:2016-09-14 10:43:16

标签: java c java-native-interface

在我开发过程中,由于错误,我错过了在函数中添加参数。但是本机jni调用中的相同函数有参数。但它仍然是从java调用精确方法。

Java类Demo.java

  package jniexamples.rmi;

class Demo {
    private native void jBoolean();
    public static void main(String[] args) {
        new Demo().jBoolean();
    }
    static {
        System.load("jnidemo.so");
    }
}

Demo.c

 #include <jni.h>
#include <stdio.h>
JNIEXPORT void JNICALL
Java_jniexamples_rmi_Demo_jBoolean(JNIEnv *env, jobject ob,jint dtype)
{
    printf("first demo %d" , dtype);
    return;
}

结果:首先演示-1579007728

即使方法签名不同,我也很困惑,它是如何调用jni方法的?

2 个答案:

答案 0 :(得分:2)

C函数仅在链接期间和运行时通过名称标识,因此Java将调用该函数但不提供任何参数。

如果在调用C函数时提供的参数太少,则会调用未定义的行为,这意味着可能发生任何事情。 (在你的情况下,你有一个看似随机的值。)

javah生成的头文件,它将具有正确签名的函数声明。如果包含生成的头,编译器可以将头中的签名与.c文件中的签名进行比较,如果它们不同,则在编译时会发出错误。

答案 1 :(得分:1)

非常短暂的情景以下一个方式展望。当您从java调用jBoolean()时,JVM注意到此方法是本机的,并尝试查找其实现。在您的情况下,JVM通过使用类和包名称装饰jBoolean来构造本机函数的名称。结果是字符串Java_jniexamples_rmi_Demo_jBoolean。然后,它尝试使用dlsym()在进程的地址空间中查找具有此名称的函数。因此,在尝试调用此函数之前,使用此函数加载本机模块至关重要。然后,如果没有问题 - dlsym()返回指向函数的指针,但请注意,此指针没有关于实际函数签名的信息,而JVM 只使用java中声明的推断本机签名。然后JVM根据推断的签名调用您的本机函数。

结果 - 推断签名和实际签名之间的差异导致未定义的行为,这可能导致非常奇怪的事情,而不仅仅是随机参数值。因此,这是一个很好的做法 - 将javah生成的标头包含在您的本机实现中。如果您的某个实现具有与标头中声明的签名不同的签名,则此类标头会中断编译错误。