从Java调用c函数
看看Java Native Interface: Getting Started。
2.1概述
[...]编写一个简单的Java应用程序调用C函数打印 的 “Hello World!”。该过程包含以下步骤:
创建声明本地方法的类(HelloWorld.java)。使用 javac编译HelloWorld源文件,产生 文件HelloWorld.class。 javac编译器随JDK或Java 2 SDK发行版一起提供。使用
javah -jni
生成包含本机方法 实现的函数原型的C头文件 (HelloWorld.h
)。 javah工具随JDK或Java 2 SDK 版本提供。编写本地 方法的C实现(HelloWorld.c
)。将C实现编译为本地库,从而创建Hello-World.dll
或libHello-World.so
。使用主机环境中可用的C编译器和链接器 。运行HelloWorld程序,使用java运行时解释器 。类文件(HelloWorld.class
) 和本机库(HelloWorld.dll
或libHelloWorld.so
)在运行时加载 。本章的其余部分将详细说明 中的这些步骤。2.2写在Java下面的程序编程语言 声明本地方法
你开始。该程序定义了一个名为HelloWorld的类,其中包含一个 本地方法print。
class HelloWorld { private native void print(); public static void main(String[] args) { new HelloWorld().print(); } static { System.loadLibrary("HelloWorld"); } }
HelloWorld类定义以print native方法的声明开始。接下来是一个主要方法, 实例化Hello-World类并调用此实例的打印本地方法 。类定义的最后一部分是一个静态的 初始化程序,用于加载包含打印本机方法的 实现的本机库。
本地方法 (如print)的声明与Java 编程语言中常规方法的声明之间有两点区别。本机方法声明必须包含 本机修饰符。本地修饰符表示此方法是以另一种语言实现的 。此外,原生方法声明 以分号(语句结束符符号 )结尾,因为 类本身没有针对本地方法的实现。我们将在一个单独的C文件中实现打印方法。
在调用本地方法打印之前,必须加载 实现打印的本地库。在这种情况下,我们在
HelloWorld
类的静态初始化程序中加载本地 库。在 调用HelloWorld
类中的任何方法之前,Java 虚拟机自动运行静态初始化程序,从而确保在调用打印本机方法之前加载了 本机库。我们定义一个能够运行
HelloWorld
类的主要方法。Hello-World.main
调用本地方法打印的方式与调用常规方法的 相同。
System.loadLibrary
需要一个库名称,找到一个与该名称对应的本地库,并将本地库加载到 应用程序中。我们将在后面的 书中讨论确切的加载过程。现在简单地记住,为了使System.loadLibrary("HelloWorld")
成功,我们需要在Win32上创建 本地库,称为HelloWorld.dll
,或者在 Solaris上创建libHelloWorld.so
。2.3编译HelloWorld类
您已经定义了HelloWorld类后,保存的源代码在 文件名为HelloWorld.java。然后用自带的JDK或Java 2 SDK版本的 javac编译器编译源文件:
javac HelloWorld.java
这个命令会生成一个
HelloWorld.class
文件在当前目录中。2.4创建的本地方法的头文件
下一步,我们将使用
javah
工具来生成JNI风格的头文件 实现C中的本地方法时有用,您可以在Hello-World
运行javah
类如下:javah -jni HelloWorld
头文件的名称是类名 具有“
.h
”追加到它的结束。上面显示的命令 会生成一个名为HelloWorld.h
的文件。我们不会在这里列出生成的 头文件。所述 头文件的最重要的部分是函数原型Java_HelloWorld_print
,这 是C函数实现该方法HelloWorld.print:JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject);
忽略
JNIEXPORT
和JNICALL
宏现在。您可能已注意到 即使本机方法的相应声明接受 无参数,本机方法的C实现也接受两个参数 。每种本地方法 实现的第一个参数是一个JNIEnv
接口指针。第二个参数是对HelloWorld
对象本身的引用 (有点像C++中的“this
” 指针)。我们将在本书稍后讨论如何使用JNIEnv
接口 指针和jobject
参数,但是这个简单的 示例忽略了这两个参数。2.5编写本地方法实现
通过
javah
产生的JNI风格的头文件可以帮助你编写C或本地方法 C++实现。您编写的函数 必须遵循生成的头文件中指定的原型。您 可以实现在C文件中HelloWorld.c
的Hello-World.print
方法 如下:#include <jni.h> #include <stdio.h> #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj) { printf("Hello World!\n"); return; }
这个本地方法的实现很简单。它使用printf函数来显示字符串“Hello World!”然后返回。如前所述,两个参数,
JNIEnv
指针和对象的引用都被忽略。C程序包括三个头文件:
jni.h
- 该头文件提供的信息的本机代码需要 调用JNI函数。在编写本地方法时,您必须始终在您的C或C++源文件中包含此文件。stdio.h
- 上面的代码 也包含stdio.h
,因为它使用printf
函数。HelloWorld.h
- 使用javah
生成的头文件。它包括用于Java_HelloWorld_print
函数的C/C++原型。 2.6编译C源代码,并创建一个本地库请记住,当你在
HelloWorld.java
文件创建的HelloWorld
类,包括你行的代码,加载本地 库到程序:System.loadLibrary("HelloWorld");
现在编写了所有必需的C代码 ,您需要编译
Hello-World.c
并构建此本机 库。不同的操作系统支持不同的方法来构建本地 库。在Solaris上,下面的命令来建立所谓libHello-World.so共享库 :
cc -G -I/java/include -I/java/include/solaris HelloWorld.c -o libHelloWorld.so
-G选项指示C编译器生成一个共享库,而不是常规的Solaris 可执行文件。由于本书中页宽的限制,我们将命令行分成两行。您需要在一行中键入命令 ,或将该命令放在脚本文件中。上
Win32
,将 以下命令生成一个动态链接库(DLL)HelloWorld.dll
使用Microsoft Visual C++编译:cl -Ic:\java\include -Ic:\java\include\win32 -MD -LD HelloWorld.c -FeHelloWorld.dll
的
-MD
选项确保HelloWorld.dll
与Win32
多线程C库链接。-LD
选项指示C编译器生成DLL而不是常规的Win32可执行文件。当然,在Solaris和Win32上, 都需要放入包含自己的设备的包含路径。2.7运行程序
在这一点上,你必须准备好运行程序的两个组成部分。 类文件(
HelloWorld.class
)调用本地方法,并且 本地库(Hello-World.dll
)实现本机方法。因为
HelloWorld
类包含自己的主要方法,您可以按以下运行 Solaris或Win32的程序:java HelloWorld
你应该看到下面的输出:
Hello World!
这是重要的是要正确设置您的本机库路径 以使程序运行。本机库路径是当加载 本机库时Java虚拟机搜索的目录列表 。如果您没有本地库路径设置 做正确,那么您将看到类似以下内容的错误:
java.lang.UnsatisfiedLinkError: no HelloWorld in library path at java.lang.Runtime.loadLibrary(Runtime.java) at java.lang.System.loadLibrary(System.java) at HelloWorld.main(HelloWorld.java)
确保本机库驻留在本地库路径的目录之一。 如果您在Solaris系统上运行,则使用环境变量
LD_LIBRARY_PATH
定义本机库路径。使 确信它包含包含文件的目录的名称。如果libHelloWorld.so
文件是在当前 目录,你可以发出在标准 壳(SH)或KornShell(KSH)建立正确的LD_LIBRARY_PATH
环境变量以下两条命令:LD_LIBRARY_PATH=. export LD_LIBRARY_PATH
等效在 的是C shell(csh或tcsh)命令如下:
setenv LD_LIBRARY_PATH .
如果您在Windows 95或 Windows NT的计算机上运行,确保
HelloWorld.dll
是在T他目前在 目录下,或者在PATH环境下列出的目录 变量中。在Java 2 SDK中1。2版本中,你也可以指定Java命令行上的本地库 路径为系统属性如下:
java -Djava.library.path=. HelloWorld
的“
-D
”命令行选项 设置一个Java平台的系统性能。将java.library.path
属性设置为“.
”会指示Java虚拟机在当前目录中搜索 本机库。
Mac用户可以忽略solaris命令。这个博客帮助我完成了这个答案帮助开始的东西:http://nerdposts.blogspot.com/2010/10/jni-mac-os-x-simple-sample.html – cloudsurfin 2015-07-15 01:31:34
您的选项包括:
Java本地接口
见:https://en.wikipedia.org/wiki/Java_Native_Interface
报价:
JNI允许程序员编写本地方法来处理情况当应用程序不能完全用Java编程语言编写时,例如当标准Java类库不支持特定于平台的功能或程序库
Java本机访问
见:https://en.wikipedia.org/wiki/Java_Native_Access
报价:
Java本机Access是一个社区开发的库,它可以让Java程序轻松访问本地共享库,而无需使用Java Nat ive接口。
JNR-FFI
见:https://github.com/jnr/jnr-ffi
报价:
JNR-FFI是加载本地库一个Java库,而无需手动编写JNI代码,或使用诸如SWIG之类的工具。
所以JNR diff JNA是? – Pacerier 2017-06-17 21:55:32
对于低于
“CL -Ic使兼容64位的DLL删除 “-MD” 从语句选项:\ java的\包括-Ic:\ java \ include \ win32 -MD -LD HelloWorld.c -FeHelloWorld.dll“
我得到了解决这个问题的方法。您需要确保的是,您正在使用64位C++编译器编译代码,以调用在64位JRE上运行的Java函数。除此之外,我们需要将创建的dll文件的路径保存在“环境变量”下的“路径”中。
如果您使用的是Windows和MinGW GCC,你可能需要额外的标志,如果你在LIB越来越UnsatisfiedLinkError具体方法:
gcc -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I"%JAVA_HOME%"\include -I"%JAVA_HOME%"\include\win32 BestCode.c -shared -o BestCode.dll
首先要确保加载您的本机库或.dll文件在类路径通过产权java.library.path
设置路径
然后使用System.loadLibrary()
Do not use .dll extension at the end.
你可能想看看JNI(Java本地接口)。 – 2011-05-11 11:13:21
我会从这些http://www.google.co.uk/search?q=JNI+tutorial – 2011-05-11 11:14:07
开始。非常感谢。 – Wen 2011-05-11 13:53:09