转载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~

运行成功!

原文地址:https://www.cnblogs.com/diyunpeng/p/1689715.html