Java调用C++知识分享
Java调用C/C++代码,最原始的方式是使用JNI(Java Native Interface),但是使用JNI技术非常痛苦,因为你需要对照要调用的C/C++函数写一个“接口”,使用SUN规定的数据结构替代C/C++语言的数据结构,调用已有的 dll/so中公布的函数。然后再在Java中载入这个“接口”,调用dll/so库中的函数。面对如此繁琐的过程的,因此诞生出了建立在经典的JNI的基础之上的一个框架–JNA( Java Native Access)。
本文主要是通过举例的方式,从代码中来到代码中去的方法,讲述JNA的使用。本文中的方法在Windows平台下以及在Centos的Linux平台下都加以验证过。因此我也才放开胆子去写。٩(๑❛ᴗ❛๑)۶。
分享开始!
下边通过一个简单的例子,让你对JNA方式完成Java调用C++有一个简单清晰的认识。
第一步:首先在pom文件中加入JNA的依赖
<dependency>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
<version>3.0.9</version>
</dependency>
第二步:编写Java方法(注意看注释的部分就行了0.0)
/**
* Created by zhonghao.han on 9/12/2018.
*/
public class JnaTest {
//继承Library,用于加载库文件
public interface Clibrary extends Library{
//windows下加载helloworld.dll链接库,Linux下加载libhelloworldLinux.so(Linux下会自动寻找lib开头的库)
Clibrary INSTANTCE = (Clibrary) Native.loadLibrary((Platform.isWindows() ? "helloworld" : "helloworldLinux"), Clibrary.class);
//此方法为链接库中的方法
void test();
}
public static void main(String[] args) {
//调用
Clibrary.INSTANTCE.test();
}
}
第三步:编写需要调用的C方法helloworld.c(C和C++都可以,都测试过0.0 这里是随手复制的一个C代码)
#include<stdio.h>
void test(){
printf("hello JNA!\n");
}
需要注意的是Native.loadLibrary第 一个参数是动态链接库dll/so的名称,但不带.dll或.so这样的后缀,这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。
第四步:将C代码编译成dll(在Windos平台下C/C++一般都是编译成.dll,在Linux下一般是编译成.so).具体的编译这里就不再细说哈哈
上例用的是Windows的平台做的演示,Linux平台的一样。
注意:库文件的位置需要放置到项目的根路径下,否则Java调用库的时候会找不到这个库文件而报如下的错误
Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'helloworld': Native library (win32-x86-64/helloworld.dll) not found in resource path
([file:/C:/hzh/SoftWares/jdk/jre/lib/charsets.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/deploy.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/ext/access-bridge-64.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/ext/cldrdata.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/ext/dnsns.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/ext/jaccess.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/ext/jfxrt.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/ext/localedata.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/ext/nashorn.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/ext/sunec.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/ext/sunjce_provider.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/ext/sunmscapi.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/ext/sunpkcs11.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/ext/zipfs.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/javaws.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/jce.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/jfr.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/jfxswt.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/jsse.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/management-agent.jar,
file:/C:/hzh/SoftWares/jdk/jre/lib/plugin.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/resources.jar, file:/C:/hzh/SoftWares/jdk/jre/lib/rt.jar,
file:/C:/hzh/codes/JNADemo/target/classes/,
file:/C:/hzh/SoftWares/Intellij/IntelliJ%20IDEA%202016.3.2/jre/jre/bin/hzh/maven/repository/net/java/dev/jna/jna/4.1.0/jna-4.1.0.jar,
file:/C:/hzh/SoftWares/Intellij/IntelliJ%20IDEA%202016.3.2/lib/idea_rt.jar])
at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:271)
at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:398)
at com.sun.jna.Library$Handler.<init>(Library.java:147)
at com.sun.jna.Native.loadLibrary(Native.java:412)
at com.sun.jna.Native.loadLibrary(Native.java:391)
at com.zhonghao.JnaTest$Clibrary.<clinit>(JnaTest.java:16)
at com.zhonghao.JnaTest.main(JnaTest.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
为什么是项目根目录呢?
Java代码搜索动态链接库路径的顺序是:先从当前类的当前文件夹找,如果没有找到再在工程当前文件夹下面找对应的dll文件,如果找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。
即:
1)项目的根路径
2)操作系统的全局路径、
3)path指定的路径。(这种尝试了未遂)
至此JNA的简单实例就结束了。
我目前只找到这一种指定库文件位置的方法,其他的方式也试了,但是未遂。方法肯定是有的,但是需要再花点时间去尝试。同样在Linux环境下,路径也是一个问题。
在Linux环境下,我尝试了指定路径等做法,未遂!然后按照windows下把库文件放到项目根目录下也不行,找不到库,报上边相同的错误。于是我将库文件放到上边错误中提到的一个路径/Desktop/JNADemo/target/classes/
下,于是就可以找到库并正常调用C/C++的方法了。目前Linux下就只发现这一种。即把库文件.so放到项目编译生成的/target/ 下。
看到这里,估计大佬们已经耐不住想要深入了解JNA了,甚至想摩拳擦掌看看有没有复杂点的例子
不要着急,下边将献上一些复杂点的C++方法接口,你先思考拿到这样的方法接口,在Java中该怎么调用?比如这个C++方法接口中需要结构体这样的参数,需要指针、函数指针这样的参数,这些在Java中该如何声明?
大餐上菜之前先说说JNA的编程过程
JNA的编程过程
在Java中加载库的接口中你需要声明要调用的函数,如test方法就是C/C++代码中的方法(文章底部附上本篇实例的完整代码,那时你可以看到全局,现在只放出局部代码)
public interface Clibrary extends Library{
//加载libhello.dll链接库
Clibrary INSTANTCE = (Clibrary) Native.loadLibrary((Platform.isWindows() ? "helloworld" : "helloworldLinux"