Java 调用 C/C++ 之 JNA 系列实战篇 —— 起步 (一)

一、为什么要写这篇文章          

        最近单位开发一些项目,要用到Java 调用 C/C++ 动态链接库,我采了JNA的调用方式,同时在开发过程中遇到过一些问题,但上网查找,发现对JNA的介绍不多,大部分只是一个初级入门,所以结合自己的实际开发,把总结出来的知识点及问题与大家分享下。在Windows 平台与 Linux平台都实现了。


二、为什么选用JNA

       Java 调用 C/C++ 动态链接库,有两种方式 JNI 与 JNA,它们的原理一样,但使用起来难易度却有区别,做个对比:

      1. JNI 比JNA难度要高一些,因为JNI要根据Java代码生成头文件,生成特殊的方法签名。再针对头文件定义的函数,用JNI 语法结合C/C++语法来实现函数,在函数中再去调用动态链接库,在这个过程中还需要去管理内存的问题(有一篇文章给大家推荐下:在 JNI 编程中避免内存泄漏)。而JNA方式完全省略了这一复杂过程。使用方便是JNA的一大特点。

         2. JNI 可以实现Java 调用 C/C++,同时 C/C++也可调用Java 。但JAN却只能Java 调用 C/C++,不具备反向调用。刚好在项目中不要用到C/C++调用JAVA,所以选择了JNA的方式。


三、 环境申明

          环境没有配好,会出现许多错误,如:找不到库文件,无法加载库文件等等,如果是刚上手,一时会找不到出错点。

我实际项目中用到的环境是(已经设置好了jdk的环境,另外在Linux平台上还要加设一变量LD_LIBRARY_PATH,它的值要等于你即将调用的so文件存放的目录,我习惯把so文件存在我的项目直接目录下,路径也就指向项目目录):

      1. windows (64位), JDK (64位),dll文件 (64位)

      2. Linux (64位),      JDK (64位),so文件 (64位)

      我的意思是要用 64位的JDK去调用64位的dll/so 文件; 32位的JDK去调用32位的dll/so 文件,如果不对应则会生如下异常信息:

      异常一:如在windows平台,用64位jdk 调用 32位的dll :Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'XxxEngine': Native library (win32-  x86-64/XxxEngine.dll) not found in resource path,它是意思是没有找到64位的dll文件,它需要的是64的文件。

      异常二:我在Linux平台上还发现有这样一个异常:Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'XxxEngine': Native library (linux-x86-64/libKwsEngine.so) not found in resource path,而我已经保证了64位的JDK调用64位的so 文件,并且Linux也是64位的操作系统,经过一番查找,是因为libXxxEngine.so文件内部引用了另外的so文件,把的依赖的文件加入就可以解决问题了,如放在/usr/local/lib 下面,此为系统默认查找库资源的路径。如何查看一个so文件依赖其它的文件方式是:ldd libXxxEngine.so。如果确定环境不再有问题,也执行过ldconfig命令,则重新启动下电脑试试。


三、官方JNA

      JNA的官方资源路径为https://github.com/twall/jna/,在这里可以下载 jna.jar 文件,以及可以查看文档JavaDoc,开发时一定要查看文档,因以它里面介绍了C Type 对应到 Java Type 数据类型。如下图



四、开发结构

        1.  把需要调用的dll/so文件放到项目的根目录下,如下图在linux环境下面libTestCF.so (在linux 下面要设置环境变量:LD_LIBRARY_PATH=/root/workspace/HelloJNA , Windows下面不用设置此环境变量),在linux下的文件为libTestCF.so,在windows下面为TestCF.dll

          

          

       2. 在libTestCF.so有要被调用的函数,在cf.h头文件有如下申明:采用C语言形式接口函数      

[cpp]  view plain  copy
  1. extern "C"  
  2. {  
  3.     Public  int TestReadCF();  
  4. }  
       3.  根据头文件编写Java代码:如图


                 

     Java代码为:

[java]  view plain  copy
  1. package cn.jna.test;  
  2.   
  3.   
  4. import com.sun.jna.Library;  
  5. import com.sun.jna.Native;  
  6.   
  7. public interface CFJna extends Library {  
  8.   
  9.     //加载动态资源库  
  10.     CFJna library = (CFJna) Native.loadLibrary("TestCF", CFJna.class);//注意库名称的写法不要含有lib字符  
  11.       
  12.     //定义要调用的方法,与cf.h头文件中定义的函数名一样  
  13.     int TestReadCF();  
  14. }  

           4. 测试调用 ,如下图:


               java 代码:

[java]  view plain  copy
  1. package cn.jna.test;  
  2.   
  3. public class CFJnaTest {  
  4.   
  5.     public static void main(String[] args) {  
  6.   
  7.         int testReadCF = CFJna.library.TestReadCF();  
  8.         System.out.println("testReadCF:"+testReadCF);  
  9.     }  
  10.   
  11. }  
文章 转载:   Java 调用 C/C++ 之 JNA 系列实战篇 —— 起步 (一)