Jon*_*nas 72
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发行版一起提供.编写本HelloWorld.c机方法的C实现().将C实现编译到本机库中,创建Hello-World.dll或libHello-World.so.使用主机环境中可用的C编译器和链接器.使用java运行时解释器运行HelloWorld程序.类文件(HelloWorld.class)和本机库(HelloWorld.dll或libHelloWorld.so)都在运行时加载.本章的其余部分将详细介绍这些步骤.2.2声明本机方法
首先,使用Java编程语言编写以下程序.该程序定义了一个名为HelloWorld的类,它包含一个本机方法print.
Run Code Online (Sandbox Code Playgroud)class HelloWorld { private native void print(); public static void main(String[] args) { new HelloWorld().print(); } static { System.loadLibrary("HelloWorld"); } }HelloWorld类定义以print native方法的声明开头.接下来是一个main方法,它实例化Hello-World类并为此实例调用print native方法.类定义的最后一部分是一个静态初始化程序,它加载包含print native方法实现的本机库.
原始方法(如print)的声明与Java编程语言中常规方法的声明之间存在两个区别.本机方法声明必须包含本机修饰符.native修饰符表示此方法是用另一种语言实现的.此外,本机方法声明以分号(语句终止符号)结束,因为类本身没有本机方法的实现.我们将在单独的C文件中实现print方法.
在调用本机方法print之前,必须加载实现print的本机库.在这种情况下,我们在
HelloWorld类的静态初始化程序中加载本机库.Java虚拟机在调用HelloWorld类中的任何方法之前自动运行静态初始化程序,从而确保在调用print本机方法之前加载本机库.我们定义了一个能够运行
HelloWorld该类的main方法.Hello-World.main以与调用常规方法相同的方式调用本机方法print.
System.loadLibrary获取库名称,找到与该名称对应的本机库,并将本机库加载到应用程序中.我们将在本书后面讨论确切的加载过程.现在只需记住,为了System.loadLibrary("HelloWorld")成功,我们需要创建一个HelloWorld.dll在Win32或libHelloWorld.soSolaris 上调用的本机库.2.3编译HelloWorld类
定义HelloWorld类之后,将源代码保存在名为HelloWorld.java的文件中.然后使用JDK或Java 2 SDK版本附带的javac编译器编译源文件:
Run Code Online (Sandbox Code Playgroud)javac HelloWorld.java此命令将
HelloWorld.class在当前目录中生成一个文件.2.4创建本机方法头文件
接下来,我们将使用该
javah工具生成JNI样式的头文件,该文件在C中实现本机方法时非常有用.您可以按如下方式javah在Hello-World类上运行 :Run Code Online (Sandbox Code Playgroud)javah -jni HelloWorld头文件的名称是类名,
.h在其末尾附加了" ".上面显示的命令生成一个名为的文件HelloWorld.h.我们不会在此完整列出生成的头文件.头文件最重要的部分是函数原型Java_HelloWorld_print,它是实现HelloWorld.print方法的C函数:Run Code Online (Sandbox Code Playgroud)JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject);暂时忽略
JNIEXPORT和JNICALL宏.您可能已经注意到,本机方法的C实现接受两个参数,即使本机方法的相应声明不接受任何参数.每个本机方法实现的第一个参数是JNIEnv接口指针.第二个参数是HelloWorld对象本身的引用(有点像thisC++中的" "指针).我们将在本书后面讨论如何使用JNIEnv接口指针和jobject参数,但是这个简单的例子忽略了这两个参数.2.5编写本机方法实现
生成的JNI样式头文件可
javah帮助您为本机方法编写C或C++实现.您编写的函数必须遵循生成的头文件中指定的-prototype.您可以Hello-World.print在C文件中实现该方法,HelloWorld.c如下所示:Run Code Online (Sandbox Code Playgroud)#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在HelloWorld.java文件中创建类时 ,您包含了一行代码,用于将本机库加载到程序中:Run Code Online (Sandbox Code Playgroud)System.loadLibrary("HelloWorld");现在已经编写了所有必需的C代码,您需要编译
Hello-World.c和构建此本机库.不同的操作系统支持不同的方法来构建本机库.在Solaris上,以下命令构建名为libHello-World.so的共享库:
Run Code Online (Sandbox Code Playgroud)cc -G -I/java/include -I/java/include/solaris HelloWorld.c -o libHelloWorld.so-G选项指示C编译器生成共享库而不是常规Solaris可执行文件.由于本书中页面宽度的限制,我们将命令行分为两行.您需要在一行中键入命令,或将命令放在脚本文件中.在
Win32,以下命令HelloWorld.dll使用Microsoft Visual C++编译器构建动态链接库(DLL):Run Code Online (Sandbox Code Playgroud)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运行程序
此时,您已准备好运行该程序的两个组件.类file(
HelloWorld.class)调用本机方法,本机库(Hello-World.dll)实现本机方法.由于
HelloWorld该类包含自己的main方法,因此可以在Solaris或Win32上运行该程序,如下所示:Run Code Online (Sandbox Code Playgroud)java HelloWorld您应该看到以下输出:
Run Code Online (Sandbox Code Playgroud)Hello World!正确设置本机库路径以使程序运行非常重要.本机库路径是Java虚拟机在加载本机库时搜索的目录列表.如果您没有正确设置本机库路径,则会看到类似于以下内容的错误:
Run Code Online (Sandbox Code Playgroud)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文件的目录的名称 .如果libHelloWorld.so文件位于当前目录中,则可以在标准shell(sh)或KornShell(ksh)中发出以下两个命令以LD_LIBRARY_PATH正确设置环境变量:Run Code Online (Sandbox Code Playgroud)LD_LIBRARY_PATH=. export LD_LIBRARY_PATHC shell(csh或tcsh)中的等效命令如下:
Run Code Online (Sandbox Code Playgroud)setenv LD_LIBRARY_PATH .如果您在Windows 95或Windows NT计算机上运行,请确保它
HelloWorld.dll位于当前目录中,或者位于PATH环境变量中列出的目录中.在Java 2 SDK 1.2发行版中,您还可以将java命令行上的本机库路径指定为系统属性,如下所示:
Run Code Online (Sandbox Code Playgroud)java -Djava.library.path=. HelloWorld"
-D"命令行选项设置Java平台系统属性.将该java.library.path属性设置为"."指示Java虚拟机在当前目录中搜索本机库.
您的选择包括:
Java Native Interface
请参阅:https://en.wikipedia.org/wiki/Java_Native_Interface
引用:
JNI使程序员能够编写本机方法来处理当应用程序无法完全用Java编程语言编写时的情况,例如,当标准Java类库不支持特定于平台的功能或程序库时
Java Native Access
请参阅:https://en.wikipedia.org/wiki/Java_Native_Access
引用:
Java Native Access是一个社区开发的库,它为Java程序提供了对本机共享库的轻松访问,而无需使用Java Native Interface.
JNR-FFI
请参阅:https://github.com/jnr/jnr-ffi
引用:
jnr-ffi是一个java库,用于加载本机库,无需手动编写JNI代码,也不使用SWIG等工具.
| 归档时间: |
|
| 查看次数: |
78879 次 |
| 最近记录: |