转载JNI(Windows)
Windows平台上使用MinGW GCC编译的JNI动态链接库会在Java加载的时候出错,本文简单介绍其中的原因和应对策略。这里只是简单示范如何用MinGW GCC去编译JNI程序。
第一步:编写java程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//Test.java public class Test { public static native void print(); static { System.loadLibrary("hello"); } public static void main(String[] args) { //打印Java加载DLL文件路径,应该包括当前目录,也就是"." //System.out. //println(System.getProperty("java.library.path")); Test.print(); } } |
注意其中的代码:public static native void print();
意思是声明print()这个方法是本地方法而且是静态方法哦,需要在jni中实现。
static{ System.loadLibrary("hello"); }
意思是载入库文件,意味着我们下面的jni程序最终需要打包成hello.dll
第二步:编译java程序
javac Test.java
第三步:生成头文件
javah -classpath .\bin -d src Test
-classpath .\bin是指定Test.class文件在当前目录的子目录bin里面
-d src 是指把生产的Test.h文件放到当前目录下的src子目录下面
第四步:编写本地实现代码
我们打开第三步生成的Test.h这个文件,找到其中的方法声明:
JNIEXPORT void JNICALL Java_Test_print(JNIEnv *, jobject);
这是jni的命名规范,具体可以参考java tutorial。这里只是方法声明,现在我们来实现它。
1 2 3 4 5 6 7 8 9 |
/* Test.cpp */ #include "Test.h" #include <iostream> using namespace std; JNIEXPORT void JNICALL Java_Test_print(JNIEnv *, jclass) { cout<<"Cpp,Java and JNI~"<<endl; } |
第五步:创建库文件
前面第一步里面提到过hello.dll,这里利用以前提到的制作动态库的命令来生成它:
g++ -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -shared -o hello.dll Test.cpp
运行:java Test
不好,出错了:
Exception in thread "main" java.lang.UnsatisfiedLinkError: print
at Test.print(Native Method)
at Test.main(HelloWorld.java:11)
意思是库文件已经成功载入了,但是没有找到相匹配的print这个方法。可是我们明明实现了这个方法的呀?原来程序在调用动态库的时候,没有我们想象中的那么简单,而且不同的编译器做法不一样,windows版java中调用jni遵从的是vc的调用方式,和我们用的MinGW GCC默认格式不一致。我们需要调整一下参数,(注意其中的–kill-at):
g++ -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -shared -Wl,--kill-at -o hello.dll Test.cpp
如果你在程序里面没有使用了C++的函数库,那么把g++换成gcc就可以了。另外,也可以把jni.h jni_md.h两个文件拷贝到源文件目录下面,这样的话就可以不用指定-I参数了,编译也更方便一些。例如,我把Test.h,Test.cpp和jni.h jni_md.h放在一起,然后可以使用命令:
g++ -shared -Wl,--kill-at -o hello.dll Test.cpp
运行:java Test
显示:Cpp,Java and JNI~
运行成功!