在我开发过程中,由于错误,我错过了在函数中添加参数。但是本机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方法的?
答案 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
生成的标头包含在您的本机实现中。如果您的某个实现具有与标头中声明的签名不同的签名,则此类标头会中断编译错误。