JNA/JNI
阅读数:181 评论数:0
跳转到新版页面分类
python/Java
正文
实现Java代码调用本地C/C++ dll/so有两种方式:
1、JNI(Java Native Interface)
如果有一个现有的.dll/.so文件,使用JNI技术调用,我们首先需要使用C语言写一个库,使用SUN规定的数据结构替代C语言的数据结构,调用已有的.dll/.so中公布的函数。
然后瑞在Java中载入这个适配器.dll/.so,再编写Java native函数作为dll中函数的代理。
经过以上两上步骤才能在Java中调用本地代码。
2、JNA(Java Native Access)
使用JNA,不需要再编写适配用的.dll/.so(用于windows时编译为xxx.dll,dynamic link library,用于Linux时编译为libxxx.so,shared object),只需要在Java中编写一个接口和一些代码,作为.dll/.so的代理,就可以在Java程序中调用dll/so。
jna是对jni的封装,是建立在JNI技术基础之上的一个Java类库,原来使用JNI,必须手工用C写一个动态链接库,在C语言中映射Java的数据类型。
JNA中,它提供了一个动态的C语言编写的转发器,可以自动实现Java和C的数据类型的映射,不再需要编写C动态链接库。
(1)C Type对应到Java Type
参考:http://java-native-access.github.io/jna/5.5.0/javadoc/
(2)技术难点
跨平台、语言调用的难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败,都是这个问题造成的。关于这一点,不论何种语言,何种技术方案,都无法解决这个问题。JNA也不例外。
因为C/C++的类型与Java的类型是不一样的,你必须转换类型让它们保持一致。
(3)JNA能完全替代JNI吗?
JNA是不能完全替代JNI的,JNA只能实现Java访问C函数,而JNI不仅可以实现Java访问C函数,也可以实现C语言调用Java代码。
使用示例:
package com.sun.jna.examples;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
/** Simple example of JNA interface mapping and usage. */
public class HelloWorld {
// This is the standard, stable way of mapping, which supports extensive
// customization and mapping of Java to native types.
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, World\n");
for (int i=0;i < args.length;i++) {
CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
}
}
}
(1)需要定义一个接口,继承自Library或StdCallLibrary,默认的是继承Library,如果动态库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary,比如众所周知的kernel32库。
(2)接口内部需要一个公共的静态常量:INSTANCE,通过这个常量,就可以获得这个接口实例,从而使用接口的方法。
该常量通过Native.loadLibrary()这个API函数获得,该函数有两个参数:
第一个参数是动态链接库dll/so的名称,但不带.dll或.so这样的后缀,这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。搜索动态链 接库路径的顺序是:先从当前类的当前文件夹找,如果没有找到,再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,如果 找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。比如上例中printf函数在Windows平台下所在的dll库名称是msvcrt,而在 其它平台如Linux下的so库名称是c。
接口中只需要定义你要用到的函数或者公共变量,注意参数返回值的类型,应该和链接库中的函数保持一致。