关闭

Java调用本地C/C++动态库拾遗 JNI/JNA与名称粉碎

717人阅读 评论(0) 收藏 举报
分类:
1、Java调用本地C/C++动态库的方法
  大概主要有两种JNI(Java Native Interface)和JNA(Java NativeAccess),最后介绍一种大招。本文没有代码,只有引用人的文章,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.dllNative.c

   
关于找不到jni.h的问题,他们在jdk的include目录下,包括jni.h、win32\jni_md.h 、win32\jawt_md.h
   将这三个问文件放在 VisualStudio.net的安装目录下的\Vc xxx \include 下,
  mingw放在安装目录下的include目录中就可以,反正跟stdlib.h这样常用的库在一起就没问题了。

  3、JNA(Java NativeAccess) 
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 条规则编写代码。

    4、跨语言调用大招
你还想要大招?告诉你一个可别打我,就是创建子进程,然后对接子进程输出流,入参通过创建进程时的附加参数传入,返回结果自然就是本应该在命令行上出现的字符串了。
   其实就是编了个可执行的命令行,然后重定向了流,用途广泛。

   5、名称粉碎(Name Mangling)
   叫名字粉碎、名字改编也一样,主要是面向对象语言对应重载的机制。
  如果用记事本打开C++的导入库文件.o/.lib,以字符而非二进制方式查看,就可以找到我们的导出函数的名称,也可以发现其粉碎规则,名称粉碎对于不同的编译器方式不同,会干扰跨语言调用。

   可以参考的文章:
       Visual C++名字修饰
       Namemangling

    6、性能损失
   最后,java损失跨平台性调用本地库换来了功能的强大,但是性能损失多少,以及在java和c++上都能做的事情是交给谁做,频繁调用本地方法好不好,都是之后有时间需要测试的。
    [补充于2012年8月]
   最近又回来做一些C#调用C++的项目,其实,对于直接调用或者起子进程再对接流的方法,效率几乎是没有太大差别的,关于这个,想一想动态库的原理就可以明白。所以在不要求绝对效率和性能的情况下,不比太过在意,只需要选择合适的方案即可

转载自:http://blog.sina.com.cn/s/blog_3f2c72ea01011kvx.html
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:12289次
    • 积分:225
    • 等级:
    • 排名:千里之外
    • 原创:7篇
    • 转载:19篇
    • 译文:1篇
    • 评论:0条
    文章存档