继续上一节,为了把这个访问c动态库的功能封装的更好,更利于在其他代码中使用,我们需要把它放到一个package里。
现在讨论一下如果把sam4java放到package com.me.test里的情况。
现在源码如下:
package com.me.test;
public class sam4java
{
public sam4java(){
}
public native int intMethod(int n);
public static void main(String[] args)
{
System.out.println("load lib sam4java start ......");
System.loadLibrary("sam4java");
System.out.println("load lib sam4java completed.");
Sam4java sam4java = new Sam4java();
int square = sam4java.intMethod(5);
System.out.println("intMethod: " + square);
}
}
我们仅仅是增加了一条语句
package com.me.test;
但是我们发现当我们重新 javac sam4java.java (成功)
运行 java sam4java (:()
无法运行了,怎么办?
其实 package com.me.test 表明了在运行的时候java会在当前目录的子目录com\me\test下执行sam4java.class,既然这样,我们就应该在执行目录下新建这个子目录
com\me\test
如果我们需要执行的目录为d:\src\sam4java,那么现在我们应该把sam4java.java拷贝到d:\src\sam4java\com\me\test,然后再
javac sam4java.java
回到d:\src\sam4java目录,
运行
java com.me.test.sam4java (:()
依然报错,不过这回报错原因应该改变了,还是进了一步,至少可以看到代码中的输出信息
load lib sam4java start ......
load lib sam4java completed.
如果sam4java.dll没有放在d:\src\sam4java目录里,那么只能看到
load lib sam4java start ......
因为一般的我们在系统环境变量中 classpath=. 表示了java执行时会查找当前目录,因此我们需要把c编写的动态库放在 d:\src\sam4java目录里
虽然已经成功装载了这个动态库,可是却无法执行动态库中的函数,什么原因?
重新生成c的头文件? javah sam4java
重新编译生成sam4java.dll
回到d:\src\sam4java目录,
运行
java com.me.test.sam4java (:()
还是一样的错误啊,怎么办?
其实,熟悉java的朋友会发现错误原因应该是在运行时无法在动态库中找到需要调用的函数,也就是说我们java代码中的函数名,java会在运行时自动转换java中的函数
sam4java.intMethod为调用c动态库的方法名称,然后在sam4java.dll中根据函数名称找到函数的入口点,调用该函数。
那么在上一文中,java把 sam4java.intMethod 转换成了 Java_sam4java_intMethod,我们可以看到成功的输出
现在我们把这个 class sam4java 放到了 package com.me.test里,java自动转换的函数名会不会有什么变化?我们的c代码的函数原形需要做什么改进吗?
的确,我们需要修改sam4java.h文件中的函数原形为
JNIEXPORT jint JNICALL Java_com_me_test_sam4java_intMethod(JNIEnv *, jobject, jint);
我们可以很容易的发现新的函数原形 Java_com_me_test_sam4java_intMethod 的秘密,就是增加了package的转换格式的前缀
com.me.test 转换为 com_me_test
并插入到原来的 Java_sam4java_intMethod 中间, 变成了 Java_com_me_test_sam4java_intMethod
最后让我们重性编译生成 sam4java.dll, 并把它放到 d:\src\sam4java
我们在d:\src\sam4java这个目录下,运行
java com.me.test.sam4java (成功)
注意:尽量不要在包名,类名和函数名里使用'_',否则会给自己带来很多的麻烦,因为在上面我们可以看到java会把包名,类名和函数名用'_'连接起来成一个新的函数名,
如果我们中间使用了'_',java将无法解释,最后就导致程序运行异常甚至无法运行,切记!