1、Java调用本地C/C++动态库的方法
大概主要有两种JNI(Java Native Interface)和JNA(Java Native Access),最后介绍一种大招。本文没有代码,只有引用人的文章,demo人家都已经写的很清楚了,我就是总结下以备自己查看。
之后说下名称粉碎(name mangling)和性能损失。
2、 JNI(Java Native Interface)
JNI是java本地化的原始方法,是java与本地语言之间调用的桥梁。注意是之间,也就是说除了Java调用本地语言动态库,本地语言(不仅限于C、C++,还有诸如Delphi、VB等)同样也可以调用Java。
其实现步骤大致分四步:
I. 编写java部分的接口代码,核心是声明native的方法,静态或实例级别均可。通过System.loadlibrary()指定需要调用的动态库名称。
II. 生成二进制文件.class,并根据.class用javah生成对应的.h头文件。
III. 编写本地代码,生成动态库。将动态库放至path变量的路径下,以供查找。
IV. java完成调用。
这里说明的是javah针对的是二进制文件.class而不是源代码文件.java,运行javah时要在最顶层包的上一级目录,输入时要连包名一并输入,程序会自动完成包和class的解析。
可供参考的文章:
使用 Java Native Interface 的最佳实践(JNI)
JNI相关参数传递
数组
调用运行时异常
找不到jni.h的解决办法(fatal error C1083: Cannot open include file: 'jni.h':)
关于一些常见的异常,尤其是C++库用mingw和cygwin的g++编译出来,可能会出现 java.lang.UnsatisfiedLinkError: XXXclass.XXXmethod()这个错误,VC则不会出现这个问题。这是由于编译时的一个奇怪的名称粉碎现象导致的,g++会在方法名前添加“@”符号,导致java调用失败,解决方法:
生成不带@的函数声明 :
gcc -Wl,--kill-at -shared -o jnihello.dll Native.c
关于找不到jni.h的问题,他们在jdk的include目录下,包括jni.h、 win32\jni_md.h 、win32\jawt_md.h
将这三个问文件放在 Visual Studio.net的安装目录下的\Vc xxx \include 下,
mingw放在安装目录下的include目录中就可以,反正跟stdlib.h这样常用的库在一起就没问题了。
3、JNA(Java Native Access)
4、跨语言调用大招
你还想要大招?告诉你一个可别打我,就是创建子进程,然后对接子进程输出流,入参通过创建进程时的附加参数传入,返回结果自然就是本应该在命令行上出现的字符串了。
其实就是编了个可执行的命令行,然后重定向了流,用途广泛。
5、名称粉碎(Name Mangling)
叫名字粉碎、名字改编也一样,主要是面向对象语言对应重载的机制。
如果用记事本打开C++的导入库文件.o/.lib,以字符而非二进制方式查看,就可以找到我们的导出函数的名称,也可以发现其粉碎规则,名称粉碎对于不同的编译器方式不同,会干扰跨语言调用。
可以参考的文章:
Visual C++名字修饰
Name mangling
6、性能损失
最后,java损失跨平台性调用本地库换来了功能的强大,但是性能损失多少,以及在java和c++上都能做的事情是交给谁做,频繁调用本地方法好不好,都是之后有时间需要测试的。
[补充于2012年8月]
最近又回来做一些C#调用C++的项目,其实,对于直接调用或者起子进程再对接流的方法,效率几乎是没有太大差别的,关于这个,想一想动态库的原理就可以明白。所以在不要求绝对效率和性能的情况下,不比太过在意,只需要选择合适的方案即可。
大概主要有两种JNI(Java Native Interface)和JNA(Java Native Access),最后介绍一种大招。本文没有代码,只有引用人的文章,demo人家都已经写的很清楚了,我就是总结下以备自己查看。
之后说下名称粉碎(name mangling)和性能损失。
2、 JNI(Java Native Interface)
JNI是java本地化的原始方法,是java与本地语言之间调用的桥梁。注意是之间,也就是说除了Java调用本地语言动态库,本地语言(不仅限于C、C++,还有诸如Delphi、VB等)同样也可以调用Java。
其实现步骤大致分四步:
I. 编写java部分的接口代码,核心是声明native的方法,静态或实例级别均可。通过System.loadlibrary()指定需要调用的动态库名称。
II. 生成二进制文件.class,并根据.class用javah生成对应的.h头文件。
III. 编写本地代码,生成动态库。将动态库放至path变量的路径下,以供查找。
IV. java完成调用。
这里说明的是javah针对的是二进制文件.class而不是源代码文件.java,运行javah时要在最顶层包的上一级目录,输入时要连包名一并输入,程序会自动完成包和class的解析。
可供参考的文章:
使用 Java Native Interface 的最佳实践(JNI)
JNI相关参数传递
数组
调用运行时异常
找不到jni.h的解决办法(fatal error C1083: Cannot open include file: 'jni.h':)
关于一些常见的异常,尤其是C++库用mingw和cygwin的g++编译出来,可能会出现 java.lang.UnsatisfiedLinkError: XXXclass.XXXmethod()这个错误,VC则不会出现这个问题。这是由于编译时的一个奇怪的名称粉碎现象导致的,g++会在方法名前添加“@”符号,导致java调用失败,解决方法:
生成不带@的函数声明 :
gcc -Wl,--kill-at -shared -o jnihello.dll Native.c
关于找不到jni.h的问题,他们在jdk的include目录下,包括jni.h、 win32\jni_md.h 、win32\jawt_md.h
将这三个问文件放在 Visual Studio.net的安装目录下的\Vc xxx \include 下,
mingw放在安装目录下的include目录中就可以,反正跟stdlib.h这样常用的库在一起就没问题了。
3、JNA(Java Native Access)
Sun官方的java本地访问项目(http://jna.java.net/),是一个库,用它可以大大简化java调用本地库的过程。这个东西有点像windows的loadlibrary和linux下的dlopen,都是动态装在库,可以在运行时根据需要装在符合接口的库。
所以,根据描述,使用方法如下:
I.编写符合函数原型的接口(interface),并根据调用方式选择是扩展StdCallLibrary接口(stdcall)还是Library接口(其他方式)。
II.编写动态库,并放至path下。C++请使用 extern "C" __declspec( dllexport ) 作为导出函数前缀,以免编译器的 名称粉碎导致java调用不能。 *
III. 通过java的jna库的Native.loadlibrary()方法装载动态库。
可供参考的文章:
JNA介绍
还是JNA的介绍和Demo
开源项目JNA-中文翻译版
比JNI简单一些,效率损失根据实际情况也可以忽略.
* 多说一句,这样编出来的C++动态库也可以在c#中被顺利调用。C#通过[DllImport]特性调用C++库时,需按照第 II 条规则编写代码。
所以,根据描述,使用方法如下:
I.编写符合函数原型的接口(interface),并根据调用方式选择是扩展StdCallLibrary接口(stdcall)还是Library接口(其他方式)。
II.编写动态库,并放至path下。C++请使用 extern "C" __declspec( dllexport ) 作为导出函数前缀,以免编译器的 名称粉碎导致java调用不能。 *
III. 通过java的jna库的Native.loadlibrary()方法装载动态库。
可供参考的文章:
JNA介绍
还是JNA的介绍和Demo
开源项目JNA-中文翻译版
比JNI简单一些,效率损失根据实际情况也可以忽略.
* 多说一句,这样编出来的C++动态库也可以在c#中被顺利调用。C#通过[DllImport]特性调用C++库时,需按照第 II 条规则编写代码。
4、跨语言调用大招
其实就是编了个可执行的命令行,然后重定向了流,用途广泛。
5、名称粉碎(Name Mangling)
叫名字粉碎、名字改编也一样,主要是面向对象语言对应重载的机制。
如果用记事本打开C++的导入库文件.o/.lib,以字符而非二进制方式查看,就可以找到我们的导出函数的名称,也可以发现其粉碎规则,名称粉碎对于不同的编译器方式不同,会干扰跨语言调用。
可以参考的文章:
Visual C++名字修饰
Name mangling
6、性能损失
最后,java损失跨平台性调用本地库换来了功能的强大,但是性能损失多少,以及在java和c++上都能做的事情是交给谁做,频繁调用本地方法好不好,都是之后有时间需要测试的。
[补充于2012年8月]
最近又回来做一些C#调用C++的项目,其实,对于直接调用或者起子进程再对接流的方法,效率几乎是没有太大差别的,关于这个,想一想动态库的原理就可以明白。所以在不要求绝对效率和性能的情况下,不比太过在意,只需要选择合适的方案即可。