JNI 官方文档翻译

来源:互联网 发布:五十知天命翻译 编辑:程序博客网 时间:2024/04/30 00:49
    1. JNI用在哪里

在你用JNI写一个项目前,返回去研究一下修改的解决方案是否更适宜是很值得的。就像上一节提到的,与用java写的应用程序相比,用JNI写的应用程序也带有JIN(如:CC++)的不安全的缺点(java没有指针,CC++有指针,指针不安全)。例如,你失去了java编程语言类型安全的优点。

一些修改方法也允许java和其他一些编程语言进行互操作。例如:

a.一个java应用程序可以用TCP/IP连接或进程间通信来于本地程序通信。

b.一个java应用程序可以通过JDBCAPI与一个传统的数据库相连。

c.Java应用程序可以利用对象等分布技术的优势,例如:JavaIDL API.

修改方案的一个共同特征是javaappnative code可以驻留在不同的进程(有时在不同的机器)。进程分开提供了一个很大的好处。。。。。。。

有时候,你可能发现javaapp与驻留在同一进程中nativecode间通行是必要的。这时JNI就要其作用了。考虑一下下面的情况:

a.。。。。。。

b.。。。。。。

c.。。。。。。

d.。。。。。。

总结一下,如果你的javaapp必须和驻留在同一进程中的nativecode互通信,你必须使用JNI来实现这一互通信功能。

    1. JNI的演变历程

Javaappnative code的交互操作在java平台的早期版本中已有应用。java平台的第一个发行版本,即JDK1.0,也包含了一个本地调用接口允许java平台调用像C++C这样的语言写的nativecode。许多第三方应用既可以实现java类库(如,java.langjava.iojava.net),也要依靠第三方应用去实现本地调用接口函数的底层主机环境。

不幸的是,JDK1.0中的本地调用函数有两个问题:

第一,

第二,

提出JNI是用来克服这些问题的。JNI是一个可以被所有JVM支持的,可以广泛实现主机环境的接口调用。JNI特征:

1.每个虚拟机的实现者可以支持大量的nativecode

2.开发工具供应商不必处理不同种类的本地方法接口。

3.最重要的是,appprogrammers只需写一个版本的nativecode ,而可以运行在不同平台上的JVM中。

JNIJDK1.1发行版中首次被支持。然而,JDK1.1任久用旧版本的本地调用方法(就像JDK1.0中的一样)去实现javaAPI,这些在JDK2.0中已经不是问题了。本地方法被重写以适应JNI标准。

JNI是一个被所有JVM实现了的本地调用接口函数。从JDK1.1开始,你就可以编写JNI程序。旧风格的本调用接口函数任就被JDK2支持,在未来更先进JVM中不再支持。

JDK2包含了许多JNI的增强,这些增强将向后兼容,JNI未来将主要完成对二进制的兼容。

    1. 程序示例

这本书包含了大量的程序示例以演示JNI的特性。

示例程序由许多用java语言和用C++C native code实现的代码段组成。有时候 nativecode SolarisWin32中进行了特画。我们在JDK2环境下展示了如何用命令行(如,javah)来编译JNI程序。

提醒一下,JNI的使用不局限于特有环境和特有应用程序开发工具。这本书注重代码实现,不是用工具编译或运行代码。捆绑JDK2的命令行工具是相当原始的做法。第三方工具提供了一个改进的用来编译JNIapp的方式。我们建议你查询与你选择的开发工具绑定的与JNI有关的帮助文档。

你可以从下面网站下载这本书上的示例源码,和这本书的最新更新,地址:

http://java.sun.com/docs/books/jni/





第二章 让我们开始吧!


这一章带你纵览用JNI写的一个简单例子。我们将写一个Javaapp来调用C程序,实现打印“HelloWorld!”。



    1. 概述

2.1演示了用JDK2写一个简单的Javaapp来调用 C程序打印“HelloWorld!”的过程。这个过程由下面这几个步骤组成:

1.创建一个类(HelloWorld.java)声明nativemethod

2.javac编译HelloWorld源文件,生成class文件HelloWorld.classJavac编译器支持JDK2.0发行版。

3.javah-jni生成 一个包含nativecode中实现的方法的原型的Cheader fileHelloWorld.h)。javah工具也由JDK2提供。

4.写一个由C实现的native程序(HelloWorld.c)。

5.c实现文件编译成一个nativelib,创建HelloWorld.dlllibHelloWorld.so。使用主机环境上的可用的C编译器和链接器。

6.使用javaruntime解释运行HelloWorld程序。class fileHelloWorld.class)和natvielibrarylibHelloWorld.so)都在runtime上运行。




Figure2.1

这章剩下的部分解释这些步骤的细节。


    1. 声明本地方法

java语言写下面的程序。

这个程序定义了一个包含一个名叫printnative方法的名叫HelloWorld的类。


ClassHelloWorld{

privatenative void print();

publicstatic void main(String[] args){

newHelloWorld().print();

}

static{

System.loadLibrary(“HelloWorld”);

}

}


HelloWorld类开始声明一个native方法。紧接着定义了一个main方法,其中实例化了一个HelloWorld对象用来调用print native方法。在类的最后一部分是一个staticinitializer,其用来加载一个实现了printnative方法的native库。

native方法的声明和一般java程序语言的声明中存在两点不同之处,如print。一个native方法声明必须包含native修饰符。native修饰符暗示这个方法用另一种语言来实现。另外,这个native方法声明用一个封号结束,这个封号说明,这个方法的实现不是在类本身中。我们将在一个独立的Cfile中实现print方法。


nativeprint方法被调用前,实现native方法的native lib必须被加载。在这个例子中,我们将用HelloWorld类的staticinitializer来加载这个nativelib。在调用HelloWorld类的任何方法前,JVM都会自动运行natvieinitializer,这样做是为了确保在调用nativeprint方法前,nativelib被加载。

我们定义main函数去运行Helloworld类。HelloWorld.main像调用常规函数一样调用printnative方法。

带有库名的System.loadLibrary定位具有相同名的native库,并且把native库加载到app中。在这本书的后面我们将讨论准确的加载过程。现在我们只需要简单记忆下为了System.loadLibrary(“HelloWorld”)成功,我们需要创建一个名叫HelloWorld.dlllibHelloWorld.sonativelib


    1. 编译HelloWorld

在你定义好HelloWorld类之后,把源文件保存到一个叫HelloWorld.java的文件中。接着用来自JDK2.0javac编译源文件:


javacHelloWorld.java

这个命令将在当前目录生成一个名叫HelloWorld.class的文件。


2.4创建native方法头文件

下面,我们将用javah工具生成一个JNI风格的头文件,头文件在用C语言实现native方法时很有用。你可以在HelloWorld类所在目录运行javah,如下:

javah-jni HelloWorld


头文件的名字,是在类名后追加一个.h。上面的命令将生成一个HelloWorld.h的头文件。头文件最重要的部分是Java_HelloWorld_print的函数原型,那是一个需用用C语言实现的HelloWorld.print方法的声明:

JNIEXPORTvoid JNICALL Java_HelloWorld_print(JNIEnv *, jobject);

我们现在暂且忽略掉宏JNIEXPORTJNICALL。你可能注意到了,C语言实现接受了两个参数,尽管对应的native方法声明并没有传递任何参数。对每一个native方法实现来说第一个参数是JNIEnv接口指针。第二个参数是HelloWorld类 自身的一个引用(就像C++中的“this”指针)。

我们将在这本书的后面讨论怎样使用JNIEnvinterface pointerjobject arg,在这个简单的例子中暂且忽略两个参数。

    1. native实现方法

JNI头文件由javah命令生成,帮助你用CC++写一个native实现方法。你写的这个函数必须和生成的头文件原型相对应。你可以用C语言在HelloWorld.c文件中实现HelloWorld.print方法,如下:

#include<jni.h>

#include<stdio.h>

#include “HelloWorld.h”


JNIEXPORTvoid JNICALL

Java_HelloWorld_print(JNIEnv*env,jobject obj)

{

printf(“HelloWorld!\n”);

return;

}

这个native方法的实现非常简单。它用printf()函数显示字符串“HelloWorld!”接着返回值。就像前面提到的,JNIEnv pinter object引用两个参数被忽略。

C程序包含三个头文件:

jni.h——这个头文件提供了调用JNIfunctionsnativecode 信息。当写native方法时,在你的CC++源文件中必须包含这个文件。

stdio.h——上面的代码片段中包含stdio.h,因为它使用printf函数。

HelloWorld.h——这个头文件是您用javah生成的。它包含用C/C++实现的Java_HelloWorld_print函数的函数原型。


2.6编译C源文件和创建Native


记得在helloWorld.java file中创建HelloWorld类时包含了一行加载native库到program的代码:

System.loadLibrary(“HelloWorld”);

目前为止,所有需要的C代码都写完了,你需要编译HelloWorld.c和 生成nativelib

不同的操作系统支持不同方式生成的nativelib。在Solaris中,用下面的命令生成一个叫libHelloWorld.so共享库:

cc-G -I/java/include -I/java/include/solaris HelloWorld.c -olibHelloWold.so


-G选项告诉c编译器生成一个共享库代替常规的Solaris执行文件。在Win32中,用下面的命令在VC++编译器中生成一个DLLHelloWorld.dll

cl-Ic:\java\include -Ic:\java\include\win32

-MD-LD HelloWorld.c -FeHelloWorld.dll

-MD选项确保HelloWorld.dll链接到Win32多线程C库中。-LD选项告诉编译器生成dll文件代替常规的Win32可执行文件。当然,Solariswin32都需要输入你在自己机器中的路径。



    1. 运行程序

在此刻,你已经拥有了运行程序所需的两个组件。.clss文件(HelloWorld.class)调用一个native方法,而nativelib libHelloWorld.so)实现了native方法。



由于HelloWorld类包含了它自己的main方法,又可以在Solariswin32上用如下方法运行这个程序:

javaHelloWorld


你应当会看到下面的输出:

HelloWorld



正确设置你的nativelib path对于运行程序很重要。当加载nativelib时,native libpath是一个供JVM查询的目录列表。如果你没有正确设置nativelib path,你将看到如下相似的错误提示:

java.lang.UnsatisfiedLinkError:no HelloWorld in library path

atjava.lang.Runtime.loadLibrary(Runtime.java)

atjava.lang.System.loadLibrary(System.java)

atHelloWorld.main(HelloWorld.java)

确保nativelib驻留在 nativelib path 中目录之一。如果你运行在一个Solaris系统,LD_LIBTARY_PATH环境变量被用来定义nativelib path。确保它包含拥有libHelloWorld.so文件的目录。如果libHelloWorld.so文件在现在的目录中,你可以在标准shellKornShell中使用下面两个命令建立LD_LIBRARY_PATH环境变量:

LD_LIBRARY_PATH= .

exportLD_LIBRARY_PATH


Cshell中用下面的命令:

setenvLD_LIBRARY_PATH .


如果你是运行在Windows95 Winddows NT上,确保HellWolrd.dll在当前目录,或者在目录列表PATH环境变量之下。

JDK2中,你可以通过如下java命令行特画nativelib path作为一个系统属性:

java-Djava.library.path = . HelloWorld

“-D”命令行选项设置了一个Java平台的系统选项。设置java.library.path属性为“.”告知JVM在当前目录下查询。



原创粉丝点击